feat: localizations
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useUserStore } from '@KTXC/stores/userStore'
|
||||
import { userService } from '@KTXC/services/user/userService'
|
||||
import { useL10n } from '@KTXC'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { t } = useL10n('user_profile')
|
||||
|
||||
const refInputEl = ref<HTMLElement>()
|
||||
const isSaving = ref(false)
|
||||
@@ -134,27 +136,27 @@ const resetAvatar = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const countries = [
|
||||
'United States',
|
||||
'United Kingdom',
|
||||
'Canada',
|
||||
'Australia',
|
||||
'Germany',
|
||||
'France',
|
||||
'India',
|
||||
'China',
|
||||
'Japan',
|
||||
'Brazil',
|
||||
'Mexico',
|
||||
'South Africa',
|
||||
]
|
||||
const countries = computed(() => [
|
||||
{ value: 'United States', title: t('account.countries.unitedStates', 'United States') },
|
||||
{ value: 'United Kingdom', title: t('account.countries.unitedKingdom', 'United Kingdom') },
|
||||
{ value: 'Canada', title: t('account.countries.canada', 'Canada') },
|
||||
{ value: 'Australia', title: t('account.countries.australia', 'Australia') },
|
||||
{ value: 'Germany', title: t('account.countries.germany', 'Germany') },
|
||||
{ value: 'France', title: t('account.countries.france', 'France') },
|
||||
{ value: 'India', title: t('account.countries.india', 'India') },
|
||||
{ value: 'China', title: t('account.countries.china', 'China') },
|
||||
{ value: 'Japan', title: t('account.countries.japan', 'Japan') },
|
||||
{ value: 'Brazil', title: t('account.countries.brazil', 'Brazil') },
|
||||
{ value: 'Mexico', title: t('account.countries.mexico', 'Mexico') },
|
||||
{ value: 'South Africa', title: t('account.countries.southAfrica', 'South Africa') },
|
||||
])
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard title="Account Details">
|
||||
<VCard :title="t('account.title', 'Account Details')">
|
||||
<VCardText class="d-flex">
|
||||
<!-- 👉 Avatar -->
|
||||
<VAvatar
|
||||
@@ -172,7 +174,7 @@ const countries = [
|
||||
@click="refInputEl?.click()"
|
||||
>
|
||||
<VIcon icon="mdi-cloud-upload" class="d-sm-none" />
|
||||
<span class="d-none d-sm-block">Upload new photo</span>
|
||||
<span class="d-none d-sm-block">{{ t('account.uploadPhoto', 'Upload new photo') }}</span>
|
||||
</VBtn>
|
||||
|
||||
<input
|
||||
@@ -190,13 +192,13 @@ const countries = [
|
||||
variant="outlined"
|
||||
@click="resetAvatar"
|
||||
>
|
||||
<span class="d-none d-sm-block">Reset</span>
|
||||
<span class="d-none d-sm-block">{{ t('actions.reset', 'Reset') }}</span>
|
||||
<VIcon icon="mdi-refresh" class="d-sm-none" />
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<p class="text-body-1 mb-0">
|
||||
Allowed JPG, GIF or PNG. Max size of 800K
|
||||
{{ t('account.photoRequirements', 'Allowed JPG, GIF or PNG. Max size of 800K') }}
|
||||
</p>
|
||||
</form>
|
||||
</VCardText>
|
||||
@@ -214,10 +216,10 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.name_given"
|
||||
placeholder="John"
|
||||
label="First Name"
|
||||
:placeholder="t('account.firstNamePlaceholder', 'John')"
|
||||
:label="t('account.firstName', 'First Name')"
|
||||
:readonly="!isFieldEditable('name_given')"
|
||||
:hint="!isFieldEditable('name_given') ? 'Managed by authentication provider' : ''"
|
||||
:hint="!isFieldEditable('name_given') ? t('account.managedFieldHint', 'Managed by authentication provider') : ''"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -229,10 +231,10 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.name_family"
|
||||
placeholder="Doe"
|
||||
label="Last Name"
|
||||
:placeholder="t('account.lastNamePlaceholder', 'Doe')"
|
||||
:label="t('account.lastName', 'Last Name')"
|
||||
:readonly="!isFieldEditable('name_family')"
|
||||
:hint="!isFieldEditable('name_family') ? 'Managed by authentication provider' : ''"
|
||||
:hint="!isFieldEditable('name_family') ? t('account.managedFieldHint', 'Managed by authentication provider') : ''"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -244,11 +246,11 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.email"
|
||||
label="E-mail"
|
||||
:label="t('account.email', 'E-mail')"
|
||||
placeholder="john.doe@example.com"
|
||||
type="email"
|
||||
:readonly="!isFieldEditable('email')"
|
||||
:hint="!isFieldEditable('email') ? 'Managed by authentication provider' : ''"
|
||||
:hint="!isFieldEditable('email') ? t('account.managedFieldHint', 'Managed by authentication provider') : ''"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -260,10 +262,10 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.phone"
|
||||
label="Phone Number"
|
||||
:label="t('account.phoneNumber', 'Phone Number')"
|
||||
placeholder="+1 (555) 123-4567"
|
||||
:readonly="!isFieldEditable('phone')"
|
||||
:hint="!isFieldEditable('phone') ? 'Managed by authentication provider' : ''"
|
||||
:hint="!isFieldEditable('phone') ? t('account.managedFieldHint', 'Managed by authentication provider') : ''"
|
||||
persistent-hint
|
||||
/>
|
||||
</VCol>
|
||||
@@ -275,8 +277,8 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.address"
|
||||
label="Address"
|
||||
placeholder="123 Main St"
|
||||
:label="t('account.address', 'Address')"
|
||||
:placeholder="t('account.addressPlaceholder', '123 Main St')"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
@@ -287,8 +289,8 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.city"
|
||||
label="City"
|
||||
placeholder="New York"
|
||||
:label="t('account.city', 'City')"
|
||||
:placeholder="t('account.cityPlaceholder', 'New York')"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
@@ -299,8 +301,8 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.state"
|
||||
label="State/Province"
|
||||
placeholder="State"
|
||||
:label="t('account.stateOrProvince', 'State/Province')"
|
||||
:placeholder="t('account.statePlaceholder', 'State')"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
@@ -311,7 +313,7 @@ const countries = [
|
||||
>
|
||||
<VTextField
|
||||
v-model="accountDataLocal.zip"
|
||||
label="Zip Code"
|
||||
:label="t('account.zipCode', 'Zip Code')"
|
||||
placeholder="10001"
|
||||
/>
|
||||
</VCol>
|
||||
@@ -323,9 +325,9 @@ const countries = [
|
||||
>
|
||||
<VSelect
|
||||
v-model="accountDataLocal.country"
|
||||
label="Country"
|
||||
:label="t('account.country', 'Country')"
|
||||
:items="countries"
|
||||
placeholder="Select Country"
|
||||
:placeholder="t('account.selectCountry', 'Select Country')"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
@@ -339,7 +341,7 @@ const countries = [
|
||||
:loading="isSaving"
|
||||
color="primary"
|
||||
>
|
||||
Save changes
|
||||
{{ t('actions.saveChanges', 'Save changes') }}
|
||||
</VBtn>
|
||||
|
||||
<VBtn
|
||||
@@ -348,7 +350,7 @@ const countries = [
|
||||
type="reset"
|
||||
@click.prevent="resetForm"
|
||||
>
|
||||
Reset
|
||||
{{ t('actions.reset', 'Reset') }}
|
||||
</VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
@@ -1,69 +1,62 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useL10n } from '@KTXC'
|
||||
|
||||
const recentDevices = ref(
|
||||
[
|
||||
{
|
||||
type: 'New for you',
|
||||
email: true,
|
||||
browser: true,
|
||||
app: true,
|
||||
},
|
||||
{
|
||||
type: 'Account activity',
|
||||
email: true,
|
||||
browser: true,
|
||||
app: true,
|
||||
},
|
||||
{
|
||||
type: 'A new browser used to sign in',
|
||||
email: true,
|
||||
browser: true,
|
||||
app: false,
|
||||
},
|
||||
{
|
||||
type: 'A new device is linked',
|
||||
email: true,
|
||||
browser: false,
|
||||
app: false,
|
||||
},
|
||||
],
|
||||
)
|
||||
const { t } = useL10n('user_profile')
|
||||
|
||||
const selectedNotification = ref('Only when I\'m online')
|
||||
const recentDevices = ref([
|
||||
{ id: 'newForYou', email: true, browser: true, app: true },
|
||||
{ id: 'accountActivity', email: true, browser: true, app: true },
|
||||
{ id: 'newBrowserSignIn', email: true, browser: true, app: false },
|
||||
{ id: 'newDeviceLinked', email: true, browser: false, app: false },
|
||||
])
|
||||
|
||||
const notificationTypeLabels = computed<Record<string, string>>(() => ({
|
||||
newForYou: t('notifications.types.newForYou', 'New for you'),
|
||||
accountActivity: t('notifications.types.accountActivity', 'Account activity'),
|
||||
newBrowserSignIn: t('notifications.types.newBrowserSignIn', 'A new browser used to sign in'),
|
||||
newDeviceLinked: t('notifications.types.newDeviceLinked', 'A new device is linked'),
|
||||
}))
|
||||
|
||||
const notificationTimingOptions = computed(() => [
|
||||
{ value: 'online', title: t('notifications.timing.online', 'Only when I\'m online') },
|
||||
{ value: 'anytime', title: t('notifications.timing.anytime', 'Anytime') },
|
||||
])
|
||||
|
||||
const selectedNotification = ref('online')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VCard title="Notifications">
|
||||
<VCard :title="t('notifications.title', 'Notifications')">
|
||||
<VCardText>
|
||||
We need permission from your browser to show notifications.
|
||||
<a href="javascript:void(0)">Request Permission</a>
|
||||
{{ t('notifications.permissionDescription', 'We need permission from your browser to show notifications.') }}
|
||||
<a href="javascript:void(0)">{{ t('notifications.requestPermission', 'Request Permission') }}</a>
|
||||
</VCardText>
|
||||
|
||||
<VTable class="text-no-wrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">
|
||||
Type
|
||||
{{ t('notifications.columns.type', 'Type') }}
|
||||
</th>
|
||||
<th scope="col">
|
||||
EMAIL
|
||||
{{ t('notifications.columns.email', 'EMAIL') }}
|
||||
</th>
|
||||
<th scope="col">
|
||||
BROWSER
|
||||
{{ t('notifications.columns.browser', 'BROWSER') }}
|
||||
</th>
|
||||
<th scope="col">
|
||||
App
|
||||
{{ t('notifications.columns.app', 'App') }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="device in recentDevices"
|
||||
:key="device.type"
|
||||
:key="device.id"
|
||||
>
|
||||
<td>
|
||||
{{ device.type }}
|
||||
{{ notificationTypeLabels[device.id] }}
|
||||
</td>
|
||||
<td>
|
||||
<VCheckbox v-model="device.email" />
|
||||
@@ -82,7 +75,7 @@ const selectedNotification = ref('Only when I\'m online')
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<p class="text-base font-weight-medium">
|
||||
When should we send you notifications?
|
||||
{{ t('notifications.timingQuestion', 'When should we send you notifications?') }}
|
||||
</p>
|
||||
|
||||
<VRow>
|
||||
@@ -93,21 +86,21 @@ const selectedNotification = ref('Only when I\'m online')
|
||||
<VSelect
|
||||
v-model="selectedNotification"
|
||||
mandatory
|
||||
:items="['Only when I\'m online', 'Anytime']"
|
||||
:items="notificationTimingOptions"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<div class="d-flex flex-wrap gap-4 mt-4">
|
||||
<VBtn type="submit">
|
||||
Save Changes
|
||||
{{ t('actions.saveChanges', 'Save changes') }}
|
||||
</VBtn>
|
||||
<VBtn
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
type="reset"
|
||||
>
|
||||
Reset
|
||||
{{ t('actions.reset', 'Reset') }}
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
import { useL10nStore, useL10n } from '@KTXC'
|
||||
|
||||
const l10nStore = useL10nStore()
|
||||
const { t } = useL10n('user_profile')
|
||||
|
||||
// Offer each available locale under its own native name
|
||||
const languageOptions = computed(() =>
|
||||
l10nStore.available.map((locale: string) => {
|
||||
let title = locale
|
||||
try {
|
||||
title = new Intl.DisplayNames([locale], { type: 'language' }).of(locale) ?? locale
|
||||
} catch {
|
||||
// unknown tag: fall back to the raw locale code
|
||||
}
|
||||
return { value: locale, title }
|
||||
})
|
||||
)
|
||||
|
||||
const selectLanguage = (locale: string | null) => {
|
||||
if (locale && locale !== l10nStore.locale) {
|
||||
l10nStore.setLocale(locale)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VCard :title="t('preferences.languageTitle', 'Language')">
|
||||
<VCardText>
|
||||
<VSelect
|
||||
:model-value="l10nStore.locale"
|
||||
:items="languageOptions"
|
||||
:label="t('preferences.languageLabel', 'Display language')"
|
||||
:hint="t('preferences.languageHint', 'Applies immediately and is saved to your account')"
|
||||
persistent-hint
|
||||
@update:model-value="selectLanguage"
|
||||
/>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</template>
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useIntegrationStore } from '@KTXC/stores/integrationStore'
|
||||
import { useL10n } from '@KTXC'
|
||||
|
||||
const integrationStore = useIntegrationStore()
|
||||
const { t } = useL10n('user_profile')
|
||||
|
||||
// Get all registered security panel components from modules
|
||||
const securityPanels = computed(() => {
|
||||
@@ -59,9 +61,11 @@ onMounted(async () => {
|
||||
class="mb-4"
|
||||
color="grey"
|
||||
/>
|
||||
<p class="text-h6 mb-2">No Security Settings Available</p>
|
||||
<p class="text-h6 mb-2">
|
||||
{{ t('security.emptyTitle', 'No Security Settings Available') }}
|
||||
</p>
|
||||
<p class="text-body-2 text-medium-emphasis">
|
||||
No security modules are currently enabled or installed.
|
||||
{{ t('security.emptyDescription', 'No security modules are currently enabled or installed.') }}
|
||||
</p>
|
||||
</VCardText>
|
||||
</VCard>
|
||||
|
||||
Reference in New Issue
Block a user