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" v-bind="validateInfos.emails"
validate-trigger="onBlur" validate-trigger="onBlur"
name="emails" 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> <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>
<div class="flex flex-col w-2/4"> <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" data-rec="true">{{ $t('labels.selectUserRole') }}</div> <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 v-model:value="usersData.role" class="nc-user-roles" dropdown-class-name="nc-dropdown-user-role">
<a-select-option <a-select-option
class="nc-role-option" class="nc-role-option"

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

@ -1,5 +1,7 @@
<script lang="ts" setup> <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<{ const props = defineProps<{
title: string title: string
@ -29,7 +31,7 @@ function renameFile(fileName: string) {
// } // }
const rules = { const rules = {
title: [{ required: true, message: 'title is required.' }], title: [{ required: true, message: t('labels.titleRequired') }],
} }
function onCancel() { function onCancel() {

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

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

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

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

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

@ -58,7 +58,7 @@ watch(
v-bind="validateInfos.emails" v-bind="validateInfos.emails"
validate-trigger="onBlur" validate-trigger="onBlur"
name="emails" name="emails"
:rules="[{ required: true, message: 'Please input email' }]" :rules="[{ required: true, message: t('msg.plsInputEmail') }]"
> >
<a-input <a-input
v-model:value="invitationUsersData.emails" v-model:value="invitationUsersData.emails"
@ -72,7 +72,7 @@ watch(
</div> </div>
<div class="flex flex-col w-1/5"> <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 <a-select
v-model:value="invitationUsersData.role" v-model:value="invitationUsersData.role"
class="!rounded-md !bg-white" 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>
<div v-if="activeView?.type === ViewTypes.FORM" class="flex flex-row justify-between"> <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> <div class="text-black">{{ $t('activity.surveyMode') }}</div>
<a-switch <a-switch
v-model:checked="surveyMode" v-model:checked="surveyMode"
v-e="['c:share:view:surver-mode:toggle']" v-e="['c:share:view:surver-mode:toggle']"
data-testid="nc-modal-share-view__surveyMode" data-testid="nc-modal-share-view__surveyMode"
> >
<!-- todo i18n -->
</a-switch> </a-switch>
</div> </div>
<div v-if="activeView?.type === ViewTypes.FORM && isEeUI" class="flex flex-row justify-between"> <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> <div class="text-black">{{ $t('activity.rtlOrientation') }}</div>
<a-switch <a-switch
v-model:checked="withRTL" v-model:checked="withRTL"
v-e="['c:share:view:rtl-orientation:toggle']" v-e="['c:share:view:rtl-orientation:toggle']"
data-testid="nc-modal-share-view__RTL" data-testid="nc-modal-share-view__RTL"
> >
<!-- todo i18n -->
</a-switch> </a-switch>
</div> </div>
<div v-if="activeView?.type === ViewTypes.FORM" class="flex flex-col justify-between gap-y-1 bg-gray-50 rounded-md"> <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="flex flex-row justify-between">
<div class="text-black">{{ $t('activity.useTheme') }}</div> <div class="text-black">{{ $t('activity.useTheme') }}</div>
<a-switch <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"> <div v-if="isInvitationLinkCopied" class="flex flex-row items-center gap-x-1">
<MdiTick class="h-3.5" /> <MdiTick class="h-3.5" />
Copied invite link {{ $t('activity.copiedInviteLink') }}
</div> </div>
<div v-else class="flex flex-row items-center gap-x-1"> <div v-else class="flex flex-row items-center gap-x-1">
<MdiContentCopy class="h-3.3" /> <MdiContentCopy class="h-3.3" />
Copy invite link {{ $t('activity.copyInviteLink') }}
</div> </div>
</a-button> </a-button>
</div> </div>

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

@ -34,9 +34,8 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<template> <template>
<a-tooltip placement="left"> <a-tooltip placement="left">
<!-- todo: i18n -->
<template #title> <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> </template>
<div <div
v-e="['c:toolbar:fullscreen']" v-e="['c:toolbar:fullscreen']"

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

@ -13,7 +13,7 @@ import { iconMap } from '#imports'
target="_blank" target="_blank"
> >
<component :is="iconMap.cloud" class="mt-[1px] text-black font-bold" /> <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>
<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" 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="p-3" @click.stop>
<div class="flex items-center"> <div class="flex items-center">
<span class="text-md font-medium text-[#212121]"> <span class="text-md font-medium text-[#212121]">
<!-- todo: i18n --> {{ $t('general.notification') }}
Notification
</span> </span>
<div class="flex-grow"></div> <div class="flex-grow"></div>
<div <div
@ -34,7 +33,7 @@ const groupType = computed({
class="cursor-pointer text-xs text-gray-500 hover:text-primary" class="cursor-pointer text-xs text-gray-500 hover:text-primary"
@click.stop="notificationStore.markAllAsRead" @click.stop="notificationStore.markAllAsRead"
> >
Mark all as read {{ $t('activity.markAllAsRead') }}
</div> </div>
</div> </div>
</div> </div>
@ -48,7 +47,7 @@ const groupType = computed({
> >
<template v-if="!notifications?.length"> <template v-if="!notifications?.length">
<div class="flex flex-col gap-2 items-center justify-center"> <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" /> <GeneralIcon icon="inbox" class="!text-40px text-gray-400" />
</div> </div>
</template> </template>

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

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

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

@ -294,8 +294,7 @@ onMounted(async () => {
await loadCommentsAndLogs() await loadCommentsAndLogs()
} catch (e: any) { } catch (e: any) {
if (e.response?.status === 404) { if (e.response?.status === 404) {
// todo: i18n message.error(t("msg.noRecordFound"))
message.error('Record not found')
router.replace({ query: {} }) router.replace({ query: {} })
} else throw e } else throw e
} }
@ -358,9 +357,9 @@ useActiveKeyupListener(
if (changedColumns.value.size > 0) { if (changedColumns.value.size > 0) {
await Modal.confirm({ await Modal.confirm({
title: 'Do you want to save the changes?', title: t('msg.saveChanges'),
okText: 'Save', okText: t('general.save'),
cancelText: 'Discard', cancelText: t('labels.discard'),
onOk: async () => { onOk: async () => {
await save() await save()
reloadHook?.trigger(null) reloadHook?.trigger(null)
@ -373,8 +372,8 @@ useActiveKeyupListener(
} else if (isNew.value) { } else if (isNew.value) {
await Modal.confirm({ await Modal.confirm({
title: 'Do you want to save the record?', title: 'Do you want to save the record?',
okText: 'Save', okText: t('general.save'),
cancelText: 'Discard', cancelText: t('labels.discard'),
onOk: async () => { onOk: async () => {
const data = await _save(rowState.value) const data = await _save(rowState.value)
await syncLTARRefs(data) await syncLTARRefs(data)
@ -402,7 +401,7 @@ const onDeleteRowClick = () => {
const onConfirmDeleteRowClick = async () => { const onConfirmDeleteRowClick = async () => {
showDeleteRowModal.value = false showDeleteRowModal.value = false
await deleteRowById(primaryKey.value) await deleteRowById(primaryKey.value)
message.success('Record deleted') message.success(t('msg.rowDeleted'))
reloadTrigger.trigger() reloadTrigger.trigger()
onClose() onClose()
showDeleteRowModal.value = false 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> </script>
<template> <template>
@ -292,23 +288,21 @@ watch(shared, () => {
@click="copyIframeCode" @click="copyIframeCode"
> >
<component :is="iconMap.embed" class="text-gray-500" /> <component :is="iconMap.embed" class="text-gray-500" />
Embed this view in your site {{ $t('labels.embedInSite') }}
</div> </div>
<div class="px-1 mt-2 flex flex-col gap-3"> <div class="px-1 mt-2 flex flex-col gap-3">
<!-- todo: i18n --> <div class="text-gray-500 border-b-1">{{ $t('general.options') }}</div>
<div class="text-gray-500 border-b-1">Options</div>
<div class="px-1 flex flex-col gap-2"> <div class="px-1 flex flex-col gap-2">
<div> <div>
<!-- Survey Mode; todo: i18n -->
<a-checkbox <a-checkbox
v-if="shared.type === ViewTypes.FORM" v-if="shared.type === ViewTypes.FORM"
v-model:checked="surveyMode" v-model:checked="surveyMode"
data-testid="nc-modal-share-view__survey-mode" data-testid="nc-modal-share-view__survey-mode"
class="!text-sm" class="!text-sm"
> >
Use Survey Mode {{ $t('general.useSurveyMode') }}
</a-checkbox> </a-checkbox>
<!-- <Transition name="layout" mode="out-in"> <!-- <Transition name="layout" mode="out-in">
@ -379,9 +373,8 @@ watch(shared, () => {
</div> </div>
<div v-if="shared.type === ViewTypes.FORM"> <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"> <a-checkbox v-model:checked="viewTheme" data-testid="nc-modal-share-view__with-theme" class="!text-sm">
Use Theme {{ $t('activity.useTheme') }}
</a-checkbox> </a-checkbox>
<Transition name="layout" mode="out-in"> <Transition name="layout" mode="out-in">
@ -400,10 +393,8 @@ watch(shared, () => {
</div> </div>
<div v-if="shared.type === ViewTypes.FORM && isRtl"> <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"> <a-checkbox v-model:checked="withRTL" data-testid="nc-modal-share-view__locale" class="!text-sm">
<!-- todo i18n --> {{ $t('activity.rtlOrientation') }}
RTL Orientation
</a-checkbox> </a-checkbox>
</div> </div>
</div> </div>

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

@ -217,7 +217,7 @@ watch(
v-bind="validateInfos.emails" v-bind="validateInfos.emails"
validate-trigger="onBlur" validate-trigger="onBlur"
name="emails" 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> <div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('datatype.Email') }}:</div>
@ -233,7 +233,7 @@ watch(
</div> </div>
<div class="flex flex-col w-1/4"> <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> <div class="ml-1 mb-1 text-xs text-gray-500">{{ $t('labels.selectUserRole') }}</div>
<a-select <a-select
@ -274,9 +274,9 @@ watch(
</div> </div>
<div class="flex flex-row justify-end gap-x-2 border-t-1 border-gray-100 pt-3"> <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 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>
</div> </div>
</GeneralModal> </GeneralModal>

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

@ -703,8 +703,7 @@ function handleUIDTChange(column, table) {
<template #extra> <template #extra>
<a-tooltip bottom> <a-tooltip bottom>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.deleteTable') }}</span>
<span>Delete Table</span>
</template> </template>
<component <component
:is="iconMap.delete" :is="iconMap.delete"
@ -805,8 +804,7 @@ function handleUIDTChange(column, table) {
<template #extra> <template #extra>
<a-tooltip bottom> <a-tooltip bottom>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.deleteTable') }}</span>
<span>Delete Table</span>
</template> </template>
<component <component
:is="iconMap.delete" :is="iconMap.delete"
@ -843,8 +841,7 @@ function handleUIDTChange(column, table) {
<template v-else-if="column.key === 'dtxp' && hasSelectColumn[tableIdx]"> <template v-else-if="column.key === 'dtxp' && hasSelectColumn[tableIdx]">
<span> <span>
<!-- TODO: i18n --> {{ $t('general.options') }}
Options
</span> </span>
</template> </template>
</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-select-option v-for="(option, i) of uiTypeOptions" :key="i" :value="option.value">
<a-tooltip placement="right"> <a-tooltip placement="right">
<template v-if="isSelectDisabled(option.label, table.columns[record.key]?._disableSelect)" #title> <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> </template>
{{ option.label }} {{ option.label }}
</a-tooltip> </a-tooltip>
@ -887,8 +888,7 @@ function handleUIDTChange(column, table) {
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-tooltip v-if="record.key === 0"> <a-tooltip v-if="record.key === 0">
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('general.primaryValue') }}</span>
<span>Primary Value</span>
</template> </template>
<div class="flex items-center float-right mr-4"> <div class="flex items-center float-right mr-4">
@ -898,8 +898,7 @@ function handleUIDTChange(column, table) {
<a-tooltip v-else> <a-tooltip v-else>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.column.delete') }}</span>
<span>Delete Column</span>
</template> </template>
<a-button type="text" @click="deleteTableColumn(tableIdx, record.key)"> <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"> <div class="mt-5 flex gap-2 justify-center">
<a-tooltip bottom> <a-tooltip bottom>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.column.addNumber') }}</span>
<span>Add Number Column</span>
</template> </template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'Number')"> <a-button class="group" @click="addNewColumnRow(tableIdx, 'Number')">
@ -928,8 +926,7 @@ function handleUIDTChange(column, table) {
<a-tooltip bottom> <a-tooltip bottom>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.column.addSingleLineText') }}</span>
<span>Add SingleLineText Column</span>
</template> </template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')"> <a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')">
@ -941,8 +938,7 @@ function handleUIDTChange(column, table) {
<a-tooltip bottom> <a-tooltip bottom>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.column.addLongText') }}</span>
<span>Add LongText Column</span>
</template> </template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'LongText')"> <a-button class="group" @click="addNewColumnRow(tableIdx, 'LongText')">
@ -954,8 +950,7 @@ function handleUIDTChange(column, table) {
<a-tooltip bottom> <a-tooltip bottom>
<template #title> <template #title>
<!-- TODO: i18n --> <span>{{ $t('activity.column.addOther') }}</span>
<span>Add Other Column</span>
</template> </template>
<a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')"> <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 { Form, Input } from 'ant-design-vue'
import type { VNodeRef } from '@vue/runtime-core' import type { VNodeRef } from '@vue/runtime-core'
import { computed } from '@vue/reactivity' 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<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
@ -12,6 +12,8 @@ const props = defineProps<{
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { t } = useI18n()
const dialogShow = useVModel(props, 'modelValue', emit) const dialogShow = useVModel(props, 'modelValue', emit)
const baseType = computed(() => props.type ?? NcProjectType.DB) const baseType = computed(() => props.type ?? NcProjectType.DB)
@ -24,7 +26,7 @@ const { navigateToProject } = useGlobal()
const nameValidationRules = [ const nameValidationRules = [
{ {
required: true, required: true,
message: 'Database name is required', message: t('msg.info.dbNameRequired'),
}, },
baseTitleValidator, baseTitleValidator,
] as RuleObject[] ] as RuleObject[]
@ -95,7 +97,11 @@ const typeLabel = computed(() => {
<!-- Create A New Table --> <!-- Create A New Table -->
<div class="flex flex-row items-center"> <div class="flex flex-row items-center">
<GeneralProjectIcon :type="baseType" class="mr-2.5 !text-lg !h-4" /> <GeneralProjectIcon :type="baseType" class="mr-2.5 !text-lg !h-4" />
Create {{ typeLabel }} {{
$t('general.createEntity', {
entity: typeLabel,
})
}}
</div> </div>
</template> </template>
<div class="mt-3"> <div class="mt-3">
@ -121,19 +127,27 @@ const typeLabel = computed(() => {
</a-form> </a-form>
<div class="flex flex-row justify-end mt-7 gap-x-2"> <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 <NcButton
v-e="['a:base:create']" v-e="['a:base:create']"
data-testid="docs-create-proj-dlg-create-btn" data-testid="docs-create-proj-dlg-create-btn"
:loading="creating" :loading="creating"
type="primary" type="primary"
:label="`Create ${typeLabel}`" :label="`${$t('general.create')} ${typeLabel}`"
:loading-label="`Creating ${typeLabel}`" :loading-label="`${$t('general.creating')} ${typeLabel}`"
@click="createProject" @click="createProject"
> >
{{ `Create ${typeLabel}` }} {{
$t('general.createEntity', {
entity: typeLabel,
})
}}
<template #loading> <template #loading>
{{ `Creating ${typeLabel}` }} {{
$t('general.creatingEntity', {
entity: typeLabel,
})
}}
</template> </template>
</NcButton> </NcButton>
</div> </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) => { validator: (rule: any, value: string, callback: (errMsg?: string) => void) => {
if (!value || value.length === 0) { if (!value || value.length === 0) {
callback('Email is required') callback(t('msg.error.signUpRules.emailRequired'))
return return
} }
const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e)) const invalidEmails = (value || '').split(/\s*,\s*/).filter((e: string) => !validateEmail(e))
if (invalidEmails.length > 0) { 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 { } else {
callback() callback()
} }
@ -119,8 +125,6 @@ const emailInput = ref((el) => {
wrap-class-name="nc-modal-invite-user" wrap-class-name="nc-modal-invite-user"
@cancel="emit('closed')" @cancel="emit('closed')"
> >
<!-- TODO: i18n -->
<div class="flex flex-col"> <div class="flex flex-col">
<div class="flex flex-row justify-between items-center pb-1.5 mb-2 border-b-1 w-full"> <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> <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-col mt-1 border-b-1 pb-5">
<div class="flex flex-row items-center pl-1.5 pb-1 h-[1.1rem]"> <div class="flex flex-row items-center pl-1.5 pb-1 h-[1.1rem]">
<MdiAccountOutline /> <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> </div>
<a-alert class="mt-1" type="success" show-icon> <a-alert class="mt-1" type="success" show-icon>
@ -193,7 +197,7 @@ const emailInput = ref((el) => {
v-bind="validateInfos.emails" v-bind="validateInfos.emails"
validate-trigger="onBlur" validate-trigger="onBlur"
name="emails" 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> <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>
<div class="flex flex-col w-2/4"> <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> <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 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 --> <!-- Create A New Table -->
<div class="prose-xl font-bold self-center my-4">{{ $t('activity.moveProject') }}</div> <div class="prose-xl font-bold self-center my-4">{{ $t('activity.moveProject') }}</div>
<!-- todo: i18n --> <div class="mb-2">{{ $t('objects.workspace') }}</div>
<div class="mb-2">Workspace</div>
<a-select v-model:value="workspaceId" class="w-full" show-search> <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"> <a-select-option v-for="workspace of ownedWorkspaces" :key="workspace.id" :value="workspace.id">
{{ workspace.title }} {{ workspace.title }}

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

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

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

@ -76,6 +76,9 @@
"whatsappTwilio": "WhatsApp Twilio", "whatsappTwilio": "WhatsApp Twilio",
"submit": "Submit", "submit": "Submit",
"create": "Create", "create": "Create",
"createEntity": "Create {entity}",
"creating": "Creating",
"creatingEntity": "Creating {entity}",
"details": "Details", "details": "Details",
"skip": "Skip", "skip": "Skip",
"duplicate": "Duplicate", "duplicate": "Duplicate",
@ -174,7 +177,14 @@
"count": "Count", "count": "Count",
"countDistinct": "Count Distinct", "countDistinct": "Count Distinct",
"sumDistinct": "Sum 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": { "objects": {
"workspace": "Workspace", "workspace": "Workspace",
@ -215,6 +225,8 @@
"editor": "Editor", "editor": "Editor",
"commenter": "Commenter", "commenter": "Commenter",
"viewer": "Viewer", "viewer": "Viewer",
"noaccess":"No Access",
"superAdmin": "Super Admin",
"orgLevelCreator": "Organization Level Creator", "orgLevelCreator": "Organization Level Creator",
"orgLevelViewer": "Organization Level Viewer" "orgLevelViewer": "Organization Level Viewer"
}, },
@ -312,8 +324,13 @@
"downloadFile": "Download File", "downloadFile": "Download File",
"renameTable": "Rename Table", "renameTable": "Rename Table",
"renamingTable": "Renaming Table", "renamingTable": "Renaming Table",
"renamingWs": "Renaming Workspace",
"renameWs": "Rename Workspace",
"deleteWs": "Delete Workspace",
"deletingWs": "Deleting Workspace",
"copyAuthToken": "Copy Auth Token", "copyAuthToken": "Copy Auth Token",
"copiedAuthToken": "Copied Auth Token", "copiedAuthToken": "Copied Auth Token",
"copyInviteToken": "Copy Invite Token",
"showSidebar": "Show Sidebar", "showSidebar": "Show Sidebar",
"hideSidebar": "Hide Sidebar", "hideSidebar": "Hide Sidebar",
"creatingTable": "Creating Table", "creatingTable": "Creating Table",
@ -373,6 +390,7 @@
"resetPasswordMenu": "Reset Password", "resetPasswordMenu": "Reset Password",
"tokens": "Tokens", "tokens": "Tokens",
"userManagement": "User Management", "userManagement": "User Management",
"accountManagement": "Account management",
"licence": "Licence", "licence": "Licence",
"allowAllMimeTypes": "Allow All Mime Types", "allowAllMimeTypes": "Allow All Mime Types",
"defaultView": "Default View", "defaultView": "Default View",
@ -550,7 +568,7 @@
"requestDataSource": "Request a data source you need?", "requestDataSource": "Request a data source you need?",
"apiKey": "API Key", "apiKey": "API Key",
"personalAccessToken": "Personal Access Token", "personalAccessToken": "Personal Access Token",
"sharedBase": "Shared Base", "sharedBaseUrl": "Shared Base URL",
"importData": "Import Data", "importData": "Import Data",
"importSecondaryViews": "Import Secondary Views", "importSecondaryViews": "Import Secondary Views",
"importRollupColumns": "Import Rollup Fields", "importRollupColumns": "Import Rollup Fields",
@ -589,7 +607,13 @@
"includeData": "Include Data", "includeData": "Include Data",
"includeView": "Include View", "includeView": "Include View",
"includeWebhook": "Include Webhook", "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": { "activity": {
"onCondition": "On Condition", "onCondition": "On Condition",
@ -622,6 +646,8 @@
"useTheme": "Use Theme", "useTheme": "Use Theme",
"copyLink": "Copy Link", "copyLink": "Copy Link",
"copiedLink": "Link Copied", "copiedLink": "Link Copied",
"copyInviteLink": "Copy invite link",
"copiedInviteLink": "Copied invite link",
"copyUrl": "Copy URL", "copyUrl": "Copy URL",
"moreColors": "More Colors", "moreColors": "More Colors",
"moveProject": "Move Base", "moveProject": "Move Base",
@ -763,6 +789,16 @@
"toggleCommentsDraw": "Toggle comments draw", "toggleCommentsDraw": "Toggle comments draw",
"expandRecord": "Expand Record", "expandRecord": "Expand Record",
"deleteRecord": "Delete 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": { "erd": {
"showColumns": "Show Fields", "showColumns": "Show Fields",
"showPkAndFk": "Show Primary and Foreign Keys", "showPkAndFk": "Show Primary and Foreign Keys",
@ -916,6 +952,7 @@
"createNewOptionNamed": "Create new option named" "createNewOptionNamed": "Create new option named"
}, },
"plsEnterANumber": "Please enter a number", "plsEnterANumber": "Please enter a number",
"plsInputEmail": "Please input email",
"invalidDate": "Invalid date", "invalidDate": "Invalid date",
"invalidLocale": "Invalid locale", "invalidLocale": "Invalid locale",
"invalidCurrencyCode": "Invalid Currency Code", "invalidCurrencyCode": "Invalid Currency Code",
@ -954,6 +991,12 @@
"areYouSureUWantTo": "Are you sure you want to delete the following", "areYouSureUWantTo": "Are you sure you want to delete the following",
"idColumnRequired": "ID field is required, you can rename this later if required.", "idColumnRequired": "ID field is required, you can rename this later if required.",
"length59Required": "The length exceeds the max 59 characters", "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": { "warning": {
"dbValid": "Please make sure database you are trying to connect is valid! This operation can cause schema loss!!", "dbValid": "Please make sure database you are trying to connect is valid! This operation can cause schema loss!!",
"barcode": { "barcode": {
@ -1113,7 +1156,19 @@
"computedFieldDeleteWarning": "contents are read-only", "computedFieldDeleteWarning": "contents are read-only",
"noMoreRecords": "No more records", "noMoreRecords": "No more records",
"tokenNameNotEmpty": "Token name should not be empty", "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": { "error": {
"nameRequired": "Name Required", "nameRequired": "Name Required",
@ -1131,7 +1186,7 @@
"dbConnectionFailed": "Connection Failure:", "dbConnectionFailed": "Connection Failure:",
"nullFilterExists": "Null filter exists. Please remove them", "nullFilterExists": "Null filter exists. Please remove them",
"signUpRules": { "signUpRules": {
"emailReqd": "Email is required", "emailRequired": "Email is required",
"emailInvalid": "Email must be valid", "emailInvalid": "Email must be valid",
"passwdRequired": "Password is required", "passwdRequired": "Password is required",
"passwdLength": "You password must be atleast 8 characters", "passwdLength": "You password must be atleast 8 characters",

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

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

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

@ -12,6 +12,7 @@ import {
provide, provide,
reactive, reactive,
ref, ref,
useI18n,
useProvideSharedFormStore, useProvideSharedFormStore,
useProvideSmartsheetStore, useProvideSmartsheetStore,
useRoute, useRoute,
@ -27,6 +28,8 @@ useSidebar('nc-left-sidebar', { hasSidebar: false })
const route = useRoute() const route = useRoute()
const { t } = useI18n()
const { loadSharedView, sharedView, sharedViewMeta, meta, notFound, password, passwordDlg, passwordError } = const { loadSharedView, sharedView, sharedViewMeta, meta, notFound, password, passwordDlg, passwordError } =
useProvideSharedFormStore(route.params.viewId as string) useProvideSharedFormStore(route.params.viewId as string)
@ -43,7 +46,7 @@ if (!notFound.value) {
applyLanguageDirection(sharedViewMeta.value.rtl ? 'rtl' : 'ltr') applyLanguageDirection(sharedViewMeta.value.rtl ? 'rtl' : 'ltr')
} else { } else {
navigateTo('/error/404') navigateTo('/error/404')
throw createError({ statusCode: 404, statusMessage: 'Page Not Found' }) throw createError({ statusCode: 404, statusMessage: t('msg.pageNotFound') })
} }
const form = reactive({ const form = reactive({
@ -74,7 +77,6 @@ watch(
@close="passwordDlg = false" @close="passwordDlg = false"
> >
<div class="w-full flex flex-col gap-4"> <div class="w-full flex flex-col gap-4">
<!-- todo: i18n -->
<h2 class="text-xl font-semibold">{{ $t('msg.thisSharedViewIsProtected') }}</h2> <h2 class="text-xl font-semibold">{{ $t('msg.thisSharedViewIsProtected') }}</h2>
<a-form layout="vertical" no-style :model="form" @finish="loadSharedView"> <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>
<div v-if="field.uidt === UITypes.LongText" class="text-sm text-gray-500 flex flex-wrap items-center"> <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 {{ $t('general.shift') }} <MdiAppleKeyboardShift class="mx-1 text-primary" /> + {{ $t('general.enter') }}
<MaterialSymbolsKeyboardReturn class="mx-1 text-primary" /> to make a line break <MaterialSymbolsKeyboardReturn class="mx-1 text-primary" /> {{ $t('msg.makeLineBreak') }}
</div> </div>
</div> </div>
</LazySmartsheetDivDataCell> </LazySmartsheetDivDataCell>
@ -345,7 +345,9 @@ onMounted(() => {
<div v-else-if="!submitted" class="flex items-center gap-3 flex-col"> <div v-else-if="!submitted" class="flex items-center gap-3 flex-col">
<a-tooltip <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-enter-delay="0.25"
:mouse-leave-delay="0" :mouse-leave-delay="0"
> >
@ -361,7 +363,7 @@ onMounted(() => {
@click="goNext()" @click="goNext()"
> >
<Transition name="fade"> <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>
<Transition name="slide-right" mode="out-in"> <Transition name="slide-right" mode="out-in">
@ -375,9 +377,8 @@ onMounted(() => {
</NcButton> </NcButton>
</a-tooltip> </a-tooltip>
<!-- todo: i18n -->
<div class="hidden md:flex text-sm text-gray-500 items-center gap-1"> <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> </div>
</div> </div>
@ -392,21 +393,21 @@ onMounted(() => {
<template v-else> <template v-else>
<div class="flex flex-col gap-1"> <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> </div>
</template> </template>
</div> </div>
<div v-if="sharedFormView" class="mt-3"> <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"> <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> </p>
<div v-if="sharedFormView?.submit_another_form" class="text-center"> <div v-if="sharedFormView?.submit_another_form" class="text-center">
<NcButton type="primary" data-testid="nc-survey-form__btn-submit-another-form" @click="resetForm"> <NcButton type="primary" data-testid="nc-survey-form__btn-submit-another-form" @click="resetForm">
Submit Another Form {{ $t('activity.submitAnotherForm') }}
</NcButton> </NcButton>
</div> </div>
</div> </div>
@ -428,7 +429,7 @@ onMounted(() => {
v-if="!submitted" 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" 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 <button
:class=" :class="
animationTarget === AnimationTarget.ArrowLeft && isAnimating animationTarget === AnimationTarget.ArrowLeft && isAnimating
@ -448,7 +449,7 @@ onMounted(() => {
</a-tooltip> </a-tooltip>
<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-enter-delay="0.25"
:mouse-leave-delay="0" :mouse-leave-delay="0"
> >

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

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

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

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

Loading…
Cancel
Save