Merge pull request #481 from SamR1/improve-dropdowns
Handle keyboard navigation on dropdowns
This commit is contained in:
commit
e3f5701b9d
4
fittrackee/dist/index.html
vendored
4
fittrackee/dist/index.html
vendored
@ -7,11 +7,11 @@
|
|||||||
<link rel="stylesheet" href="/static/css/fork-awesome.min.css"/>
|
<link rel="stylesheet" href="/static/css/fork-awesome.min.css"/>
|
||||||
<link rel="stylesheet" href="/static/css/leaflet.css"/>
|
<link rel="stylesheet" href="/static/css/leaflet.css"/>
|
||||||
<title>FitTrackee</title>
|
<title>FitTrackee</title>
|
||||||
<script type="module" crossorigin src="/static/index-omYyfw-i.js"></script>
|
<script type="module" crossorigin src="/static/index-GyLsc4kt.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/static/charts-_RwsDDkL.js">
|
<link rel="modulepreload" crossorigin href="/static/charts-_RwsDDkL.js">
|
||||||
<link rel="modulepreload" crossorigin href="/static/maps-ZyuCPqes.js">
|
<link rel="modulepreload" crossorigin href="/static/maps-ZyuCPqes.js">
|
||||||
<link rel="stylesheet" crossorigin href="/static/css/maps-B7qTrBCW.css">
|
<link rel="stylesheet" crossorigin href="/static/css/maps-B7qTrBCW.css">
|
||||||
<link rel="stylesheet" crossorigin href="/static/css/index-sAkBlxb8.css">
|
<link rel="stylesheet" crossorigin href="/static/css/index-PPVfBuiJ.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
1
fittrackee/dist/static/css/index-PPVfBuiJ.css
vendored
Normal file
1
fittrackee/dist/static/css/index-PPVfBuiJ.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,23 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dropdown-wrapper">
|
<div class="dropdown-wrapper">
|
||||||
<button
|
<button
|
||||||
:aria-label="buttonLabel"
|
aria-controls="dropdown-list"
|
||||||
:aria-expanded="isOpen"
|
:aria-expanded="isOpen"
|
||||||
|
aria-haspopup="true"
|
||||||
|
:aria-label="buttonLabel"
|
||||||
class="dropdown-selector transparent"
|
class="dropdown-selector transparent"
|
||||||
@click="toggleDropdown"
|
@click="toggleDropdown()"
|
||||||
|
ref="dropdownButton"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-list" v-if="isOpen">
|
<ul
|
||||||
|
v-if="isOpen"
|
||||||
|
:aria-labelledby="listLabel"
|
||||||
|
class="dropdown-list"
|
||||||
|
id="dropdown-list"
|
||||||
|
role="menu"
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
:class="{ selected: option.value === selected }"
|
:class="{
|
||||||
v-for="(option, index) in dropdownOptions"
|
selected: option.value === selected,
|
||||||
|
focused: index === focusOptionIndex,
|
||||||
|
}"
|
||||||
|
v-for="(option, index) in options"
|
||||||
:key="index"
|
:key="index"
|
||||||
tabindex="0"
|
:id="`dropdown-item-${index}`"
|
||||||
|
tabindex="-1"
|
||||||
@click="updateSelected(option)"
|
@click="updateSelected(option)"
|
||||||
@keydown.enter="updateSelected(option)"
|
@keydown.enter="updateSelected(option)"
|
||||||
role="button"
|
@mouseover="onMouseOver(index)"
|
||||||
|
role="menuitem"
|
||||||
>
|
>
|
||||||
{{ option.label }}
|
{{ option.label }}
|
||||||
</li>
|
</li>
|
||||||
@ -26,7 +40,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, toRefs, watch } from 'vue'
|
import { type Ref, ref, onMounted, onUnmounted, toRefs, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
import type { IDropdownOption, TDropdownOptions } from '@/types/forms'
|
import type { IDropdownOption, TDropdownOptions } from '@/types/forms'
|
||||||
@ -34,9 +48,10 @@
|
|||||||
options: TDropdownOptions
|
options: TDropdownOptions
|
||||||
selected: string
|
selected: string
|
||||||
buttonLabel: string
|
buttonLabel: string
|
||||||
|
listLabel: string
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const { options } = toRefs(props)
|
const { options, selected } = toRefs(props)
|
||||||
|
|
||||||
const emit = defineEmits({
|
const emit = defineEmits({
|
||||||
selected: (option: IDropdownOption) => option,
|
selected: (option: IDropdownOption) => option,
|
||||||
@ -44,20 +59,93 @@
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const dropdownOptions = options.value.map((option) => option)
|
const dropdownButton: Ref<HTMLButtonElement | null> = ref(null)
|
||||||
|
const focusOptionIndex: Ref<number> = ref(
|
||||||
|
getIndexFromOptionValue(selected.value)
|
||||||
|
)
|
||||||
|
|
||||||
function toggleDropdown() {
|
function toggleDropdown() {
|
||||||
isOpen.value = !isOpen.value
|
if (isOpen.value) {
|
||||||
|
closeDropdown()
|
||||||
|
} else {
|
||||||
|
isOpen.value = true
|
||||||
|
const option = document.getElementById(
|
||||||
|
`dropdown-item-${focusOptionIndex.value}`
|
||||||
|
)
|
||||||
|
option?.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function closeDropdown() {
|
||||||
|
isOpen.value = false
|
||||||
|
focusOptionIndex.value = getIndexFromOptionValue(selected.value)
|
||||||
|
dropdownButton.value?.focus()
|
||||||
}
|
}
|
||||||
function updateSelected(option: IDropdownOption) {
|
function updateSelected(option: IDropdownOption) {
|
||||||
emit('selected', option)
|
emit('selected', option)
|
||||||
isOpen.value = false
|
isOpen.value = false
|
||||||
}
|
}
|
||||||
|
function getIndexFromOptionValue(value: string) {
|
||||||
|
const index = options.value.findIndex((o) => o.value === value)
|
||||||
|
return index >= 0 ? index : 0
|
||||||
|
}
|
||||||
|
function handleKey(e: KeyboardEvent) {
|
||||||
|
let prevent = false
|
||||||
|
if (isOpen.value) {
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
prevent = true
|
||||||
|
focusOptionIndex.value += 1
|
||||||
|
if (focusOptionIndex.value > options.value.length) {
|
||||||
|
focusOptionIndex.value = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
prevent = true
|
||||||
|
focusOptionIndex.value -= 1
|
||||||
|
if (focusOptionIndex.value < 0) {
|
||||||
|
focusOptionIndex.value = options.value.length - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === 'Home') {
|
||||||
|
prevent = true
|
||||||
|
focusOptionIndex.value = 0
|
||||||
|
}
|
||||||
|
if (e.key === 'End') {
|
||||||
|
prevent = true
|
||||||
|
focusOptionIndex.value = options.value.length - 1
|
||||||
|
}
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
prevent = true
|
||||||
|
updateSelected(options.value[focusOptionIndex.value])
|
||||||
|
}
|
||||||
|
if (e.key === 'Escape' || e.key === 'Tab') {
|
||||||
|
prevent = e.key === 'Escape'
|
||||||
|
closeDropdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prevent) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onMouseOver(index: number) {
|
||||||
|
focusOptionIndex.value = index
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.path,
|
() => route.path,
|
||||||
() => (isOpen.value = false)
|
() => (isOpen.value = false)
|
||||||
)
|
)
|
||||||
|
watch(
|
||||||
|
() => selected.value,
|
||||||
|
(value) => (focusOptionIndex.value = getIndexFromOptionValue(value))
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('keydown', handleKey)
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('keydown', handleKey)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -90,7 +178,8 @@
|
|||||||
content: ' ✔';
|
content: ' ✔';
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
|
&.focused {
|
||||||
background-color: var(--dropdown-hover-color);
|
background-color: var(--dropdown-hover-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,9 @@
|
|||||||
:selected="language"
|
:selected="language"
|
||||||
@selected="updateLanguage"
|
@selected="updateLanguage"
|
||||||
:buttonLabel="$t('user.LANGUAGE')"
|
:buttonLabel="$t('user.LANGUAGE')"
|
||||||
|
:listLabel="$t('user.LANGUAGE', 0)"
|
||||||
>
|
>
|
||||||
<i class="fa fa-language"></i>
|
<i class="fa fa-language" aria-hidden="true"></i>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,19 +7,35 @@
|
|||||||
:value="timezone"
|
:value="timezone"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
required
|
required
|
||||||
@keydown.esc="onUpdateTimezone(input)"
|
role="combobox"
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-controls="tz-dropdown-list"
|
||||||
|
:aria-expanded="isOpen"
|
||||||
|
@keydown.esc="cancelUpdate()"
|
||||||
@keydown.enter="onEnter"
|
@keydown.enter="onEnter"
|
||||||
@input="openDropdown"
|
@input="openDropdown"
|
||||||
|
@blur="closeDropdown()"
|
||||||
|
@keydown.down="onKeyDown()"
|
||||||
|
@keydown.up="onKeyUp()"
|
||||||
/>
|
/>
|
||||||
<ul class="tz-dropdown-list" v-if="isOpen" ref="tzList">
|
<ul
|
||||||
|
class="tz-dropdown-list"
|
||||||
|
id="tz-dropdown-list"
|
||||||
|
v-if="isOpen"
|
||||||
|
role="listbox"
|
||||||
|
tabindex="-1"
|
||||||
|
:aria-label="$t('user.PROFILE.TIMEZONE', 0)"
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
v-for="(tz, index) in timeZones.filter((t) => matchTimezone(t))"
|
v-for="(tz, index) in filteredTimezones"
|
||||||
:key="tz"
|
:key="tz"
|
||||||
|
:id="`tz-dropdown-item-${index}`"
|
||||||
class="tz-dropdown-item"
|
class="tz-dropdown-item"
|
||||||
:class="{ focus: index === focusItemIndex }"
|
:class="{ focus: index === focusItemIndex }"
|
||||||
@click="onUpdateTimezone(tz)"
|
@click="onUpdateTimezone(index)"
|
||||||
@mouseover="onMouseOver(index)"
|
@mouseover="onMouseOver(index)"
|
||||||
:autofocus="index === focusItemIndex"
|
:autofocus="index === focusItemIndex"
|
||||||
|
role="option"
|
||||||
>
|
>
|
||||||
{{ tz }}
|
{{ tz }}
|
||||||
</li>
|
</li>
|
||||||
@ -28,8 +44,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, toRefs, watch } from 'vue'
|
import { computed, ref, toRefs, watch } from 'vue'
|
||||||
import type { Ref } from 'vue'
|
import type { ComputedRef, Ref } from 'vue'
|
||||||
|
|
||||||
import { timeZones } from '@/utils/timezone'
|
import { timeZones } from '@/utils/timezone'
|
||||||
|
|
||||||
@ -46,8 +62,10 @@
|
|||||||
const { input, disabled } = toRefs(props)
|
const { input, disabled } = toRefs(props)
|
||||||
const timezone: Ref<string> = ref(input.value)
|
const timezone: Ref<string> = ref(input.value)
|
||||||
const isOpen: Ref<boolean> = ref(false)
|
const isOpen: Ref<boolean> = ref(false)
|
||||||
const tzList: Ref<HTMLInputElement | null> = ref(null)
|
|
||||||
const focusItemIndex: Ref<number> = ref(0)
|
const focusItemIndex: Ref<number> = ref(0)
|
||||||
|
const filteredTimezones: ComputedRef<string[]> = computed(() =>
|
||||||
|
input.value ? timeZones.filter((t) => matchTimezone(t)) : timeZones
|
||||||
|
)
|
||||||
|
|
||||||
function matchTimezone(t: string): RegExpMatchArray | null {
|
function matchTimezone(t: string): RegExpMatchArray | null {
|
||||||
return t.toLowerCase().match(timezone.value.toLowerCase())
|
return t.toLowerCase().match(timezone.value.toLowerCase())
|
||||||
@ -55,15 +73,17 @@
|
|||||||
function onMouseOver(index: number) {
|
function onMouseOver(index: number) {
|
||||||
focusItemIndex.value = index
|
focusItemIndex.value = index
|
||||||
}
|
}
|
||||||
function onUpdateTimezone(value: string) {
|
function onUpdateTimezone(index: number) {
|
||||||
timezone.value = value
|
if (filteredTimezones.value.length > index) {
|
||||||
|
timezone.value = filteredTimezones.value[index]
|
||||||
|
emit('updateTimezone', timezone.value)
|
||||||
isOpen.value = false
|
isOpen.value = false
|
||||||
emit('updateTimezone', value)
|
}
|
||||||
}
|
}
|
||||||
function onEnter(event: Event) {
|
function onEnter(event: Event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (tzList.value?.firstElementChild?.innerHTML) {
|
if (filteredTimezones.value.length > 0) {
|
||||||
onUpdateTimezone(tzList.value?.firstElementChild?.innerHTML)
|
onUpdateTimezone(focusItemIndex.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function openDropdown(event: Event) {
|
function openDropdown(event: Event) {
|
||||||
@ -71,6 +91,42 @@
|
|||||||
isOpen.value = true
|
isOpen.value = true
|
||||||
timezone.value = (event.target as HTMLInputElement).value.trim()
|
timezone.value = (event.target as HTMLInputElement).value.trim()
|
||||||
}
|
}
|
||||||
|
function closeDropdown() {
|
||||||
|
onUpdateTimezone(focusItemIndex.value)
|
||||||
|
}
|
||||||
|
function scrollIntoOption(index: number) {
|
||||||
|
const option = document.getElementById(`tz-dropdown-item-${index}`)
|
||||||
|
if (option) {
|
||||||
|
option.focus()
|
||||||
|
option.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onKeyDown() {
|
||||||
|
isOpen.value = true
|
||||||
|
focusItemIndex.value =
|
||||||
|
focusItemIndex.value === null ? 0 : (focusItemIndex.value += 1)
|
||||||
|
if (focusItemIndex.value >= filteredTimezones.value.length) {
|
||||||
|
focusItemIndex.value = 0
|
||||||
|
}
|
||||||
|
scrollIntoOption(focusItemIndex.value)
|
||||||
|
}
|
||||||
|
function onKeyUp() {
|
||||||
|
isOpen.value = true
|
||||||
|
focusItemIndex.value =
|
||||||
|
focusItemIndex.value === null
|
||||||
|
? filteredTimezones.value.length - 1
|
||||||
|
: (focusItemIndex.value -= 1)
|
||||||
|
if (focusItemIndex.value <= -1) {
|
||||||
|
focusItemIndex.value = filteredTimezones.value.length - 1
|
||||||
|
}
|
||||||
|
scrollIntoOption(focusItemIndex.value)
|
||||||
|
}
|
||||||
|
function cancelUpdate() {
|
||||||
|
if (isOpen.value) {
|
||||||
|
isOpen.value = false
|
||||||
|
timezone.value = input.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.input,
|
() => props.input,
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "Passwort verbergen",
|
"HIDE_PASSWORD": "Passwort verbergen",
|
||||||
"INVALID_TOKEN": "Ungültiges Token, bitte fordere ein neues Passworts an.",
|
"INVALID_TOKEN": "Ungültiges Token, bitte fordere ein neues Passworts an.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Ich möchte meinen Account löschen",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Ich möchte meinen Account löschen",
|
||||||
"LANGUAGE": "Sprache",
|
"LANGUAGE": "Sprache | Sprachen",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Die Datenschutzrichtlinie wurde aktualisiert, bitte {0} sie vor dem Fortfahren.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Die Datenschutzrichtlinie wurde aktualisiert, bitte {0} sie vor dem Fortfahren.",
|
||||||
"LOGIN": "Anmeldung",
|
"LOGIN": "Anmeldung",
|
||||||
"LOGOUT": "Abmelden",
|
"LOGOUT": "Abmelden",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "hide password",
|
"HIDE_PASSWORD": "hide password",
|
||||||
"INVALID_TOKEN": "Invalid token, please request a new password reset.",
|
"INVALID_TOKEN": "Invalid token, please request a new password reset.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "I want to delete my account",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "I want to delete my account",
|
||||||
"LANGUAGE": "Language",
|
"LANGUAGE": "Language | Languages",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "The privacy policy has been updated, please {0} it before proceeding.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "The privacy policy has been updated, please {0} it before proceeding.",
|
||||||
"LOGIN": "Login",
|
"LOGIN": "Login",
|
||||||
"LOGOUT": "Logout",
|
"LOGOUT": "Logout",
|
||||||
@ -123,7 +123,7 @@
|
|||||||
"LIGHT": "Light"
|
"LIGHT": "Light"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TIMEZONE": "Timezone",
|
"TIMEZONE": "Timezone | Timezones",
|
||||||
"UNITS": {
|
"UNITS": {
|
||||||
"IMPERIAL": "Imperial system (ft, mi, mph, °F)",
|
"IMPERIAL": "Imperial system (ft, mi, mph, °F)",
|
||||||
"LABEL": "Units for distance",
|
"LABEL": "Units for distance",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "ocultar contraseña",
|
"HIDE_PASSWORD": "ocultar contraseña",
|
||||||
"INVALID_TOKEN": "Clave secreta no válida, solicita un nuevo restablecimiento de contraseña.",
|
"INVALID_TOKEN": "Clave secreta no válida, solicita un nuevo restablecimiento de contraseña.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Quiero eliminar mi cuenta",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Quiero eliminar mi cuenta",
|
||||||
"LANGUAGE": "Idioma",
|
"LANGUAGE": "Idioma | Idiomas",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "La política de privacidad ha sido actualizada, {0} antes de continuar.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "La política de privacidad ha sido actualizada, {0} antes de continuar.",
|
||||||
"LOGIN": "Acceder",
|
"LOGIN": "Acceder",
|
||||||
"LOGOUT": "Cerrar sesión",
|
"LOGOUT": "Cerrar sesión",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "masquer le mot de passe",
|
"HIDE_PASSWORD": "masquer le mot de passe",
|
||||||
"INVALID_TOKEN": "Jeton invalide, veuillez demander une nouvelle réinitialisation de mot de passe.",
|
"INVALID_TOKEN": "Jeton invalide, veuillez demander une nouvelle réinitialisation de mot de passe.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Je souhaite supprimer mon compte",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Je souhaite supprimer mon compte",
|
||||||
"LANGUAGE": "Langue",
|
"LANGUAGE": "Langue | Langues",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "La politique de confidentialité a été mise à jour. Veuillez l'{0} avant de poursuivre.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "La politique de confidentialité a été mise à jour. Veuillez l'{0} avant de poursuivre.",
|
||||||
"LOGIN": "Se connecter",
|
"LOGIN": "Se connecter",
|
||||||
"LOGOUT": "Se déconnecter",
|
"LOGOUT": "Se déconnecter",
|
||||||
@ -123,7 +123,7 @@
|
|||||||
"LIGHT": "Clair"
|
"LIGHT": "Clair"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TIMEZONE": "Fuseau horaire",
|
"TIMEZONE": "Fuseau horaire | Fuseaux horaires",
|
||||||
"UNITS": {
|
"UNITS": {
|
||||||
"IMPERIAL": "Système impérial (ft, mi, mph, °F)",
|
"IMPERIAL": "Système impérial (ft, mi, mph, °F)",
|
||||||
"LABEL": "Unités pour les distances",
|
"LABEL": "Unités pour les distances",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "agochar contrasinal",
|
"HIDE_PASSWORD": "agochar contrasinal",
|
||||||
"INVALID_TOKEN": "Token non válido, solicita un novo restablecemento de contrasinal.",
|
"INVALID_TOKEN": "Token non válido, solicita un novo restablecemento de contrasinal.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Quero eliminar a miña conta",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Quero eliminar a miña conta",
|
||||||
"LANGUAGE": "Idioma",
|
"LANGUAGE": "Idioma | Idiomas",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Actualizouse a política de privacidade, podes {0} antes de continuar.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Actualizouse a política de privacidade, podes {0} antes de continuar.",
|
||||||
"LOGIN": "Acceso",
|
"LOGIN": "Acceso",
|
||||||
"LOGOUT": "Pechar sesión",
|
"LOGOUT": "Pechar sesión",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"FILTER_ON_USERNAME": "Filtra per username",
|
"FILTER_ON_USERNAME": "Filtra per username",
|
||||||
"HIDE_PASSWORD": "nascondi password",
|
"HIDE_PASSWORD": "nascondi password",
|
||||||
"INVALID_TOKEN": "Token invalido, per favore richiedi un nuovo reset della password.",
|
"INVALID_TOKEN": "Token invalido, per favore richiedi un nuovo reset della password.",
|
||||||
"LANGUAGE": "Lingua",
|
"LANGUAGE": "Lingua | Le lingue",
|
||||||
"LOGIN": "Login",
|
"LOGIN": "Login",
|
||||||
"LOGOUT": "Logout",
|
"LOGOUT": "Logout",
|
||||||
"LOG_IN": "log in",
|
"LOG_IN": "log in",
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"HIDE_PASSWORD": "skjul passord",
|
"HIDE_PASSWORD": "skjul passord",
|
||||||
"INVALID_TOKEN": "Ugyldig symbol. Forespør ny tilbakestilling av passord.",
|
"INVALID_TOKEN": "Ugyldig symbol. Forespør ny tilbakestilling av passord.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Jeg vil slette kontoen min",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Jeg vil slette kontoen min",
|
||||||
"LANGUAGE": "Språk",
|
"LANGUAGE": "Språk | Språk",
|
||||||
"LOGIN": "Logg inn",
|
"LOGIN": "Logg inn",
|
||||||
"LOGOUT": "Logg ut",
|
"LOGOUT": "Logg ut",
|
||||||
"LOGOUT_CONFIRMATION": "Vil du logge ut?",
|
"LOGOUT_CONFIRMATION": "Vil du logge ut?",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "verberg wachtwoord",
|
"HIDE_PASSWORD": "verberg wachtwoord",
|
||||||
"INVALID_TOKEN": "Ongeldig token, vraag een nieuwe wachtwoord reset aan.",
|
"INVALID_TOKEN": "Ongeldig token, vraag een nieuwe wachtwoord reset aan.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Ik wil mijn account verwijderen",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Ik wil mijn account verwijderen",
|
||||||
"LANGUAGE": "Taal",
|
"LANGUAGE": "Taal | Talen",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Het privacybeleid werd aangepast, gelieve te {0} voor verdergaan.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Het privacybeleid werd aangepast, gelieve te {0} voor verdergaan.",
|
||||||
"LOGIN": "Inloggen",
|
"LOGIN": "Inloggen",
|
||||||
"LOGOUT": "Uitloggen",
|
"LOGOUT": "Uitloggen",
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
"HIDE_PASSWORD": "ukryj hasło",
|
"HIDE_PASSWORD": "ukryj hasło",
|
||||||
"INVALID_TOKEN": "Niepoprawny token, proszę zlecić nowy reset hasła.",
|
"INVALID_TOKEN": "Niepoprawny token, proszę zlecić nowy reset hasła.",
|
||||||
"I_WANT_TO_DELETE_MY_ACCOUNT": "Chcę usunąć swoje konto",
|
"I_WANT_TO_DELETE_MY_ACCOUNT": "Chcę usunąć swoje konto",
|
||||||
"LANGUAGE": "Język",
|
"LANGUAGE": "Język | Języki",
|
||||||
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Polityka prywatności uległa zmianie, proszę {0} ją przed przejściem dalej.",
|
"LAST_PRIVACY_POLICY_TO_VALIDATE": "Polityka prywatności uległa zmianie, proszę {0} ją przed przejściem dalej.",
|
||||||
"LOGIN": "Zaloguj",
|
"LOGIN": "Zaloguj",
|
||||||
"LOGOUT": "Wyloguj",
|
"LOGOUT": "Wyloguj",
|
||||||
|
Loading…
Reference in New Issue
Block a user