mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
feat(locale): provide dir on defineLocale (#2620)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -17,6 +17,10 @@ export default defineCommand({
|
||||
name: {
|
||||
description: 'Locale name to create. For example: English.',
|
||||
required: true
|
||||
},
|
||||
dir: {
|
||||
description: 'Locale direction. For example: rtl.',
|
||||
default: 'ltr'
|
||||
}
|
||||
},
|
||||
async setup({ args }) {
|
||||
@@ -32,6 +36,11 @@ export default defineCommand({
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (!['ltr', 'rtl'].includes(args.dir)) {
|
||||
consola.error(`🚨 Direction ${args.dir} not supported!`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (!args.code.match(/^[a-z]{2}(?:_[a-z]{2,4})?$/)) {
|
||||
consola.error(`🚨 ${args.code} is not a valid locale code!\nExample: en or en_us`)
|
||||
process.exit(1)
|
||||
@@ -45,7 +54,9 @@ export default defineCommand({
|
||||
// Create new locale file
|
||||
await fsp.copyFile(originLocaleFilePath, newLocaleFilePath)
|
||||
const localeFile = await fsp.readFile(newLocaleFilePath, 'utf-8')
|
||||
const rewrittenLocaleFile = localeFile.replace(/defineLocale\('(.*)'/, `defineLocale('${args.name}', '${normalizeLocale(args.code)}'`)
|
||||
const rewrittenLocaleFile = localeFile
|
||||
.replace(/name: '(.*)',/, `name: '${args.name}',`)
|
||||
.replace(/code: '(.*)',/, `code: '${normalizeLocale(args.code)}',${(args.dir && args.dir !== 'ltr') ? `\n dir: '${args.dir}',` : ''}`)
|
||||
await fsp.writeFile(newLocaleFilePath, rewrittenLocaleFile)
|
||||
|
||||
consola.success(`🪄 Generated ${newLocaleFilePath}`)
|
||||
|
||||
@@ -12,11 +12,13 @@ select:
|
||||
to: /getting-started/i18n/vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
::note{to="/components/app"}
|
||||
Nuxt UI provides an [App](/components/app) component that wraps your app to provide global configurations.
|
||||
::
|
||||
|
||||
## Locale
|
||||
### Locale
|
||||
|
||||
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
|
||||
|
||||
@@ -32,14 +34,36 @@ import { fr } from '@nuxt/ui/locale'
|
||||
</template>
|
||||
```
|
||||
|
||||
### Direction
|
||||
|
||||
Each locale has a default direction, but you can override it using the `dir` prop if needed.
|
||||
|
||||
Use the `dir` prop with `ltr` or `rtl` to set the global reading direction of your app:
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
import { fr } from '@nuxt/ui/locale'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp dir="rtl" :locale="fr">
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Custom locale
|
||||
|
||||
You also have the option to add your own locale using `defineLocale`:
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
const locale = defineLocale('My custom locale', 'en', {
|
||||
// implement pairs
|
||||
const locale = defineLocale({
|
||||
name: 'My custom locale',
|
||||
code: 'en',
|
||||
messages: {
|
||||
// implement pairs
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -51,7 +75,7 @@ const locale = defineLocale('My custom locale', 'en', {
|
||||
```
|
||||
|
||||
::tip
|
||||
Look at the second parameter, there you need to pass the iso code of the language. Example:
|
||||
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
|
||||
* `hi` Hindi (language)
|
||||
* `de-AT`: German (language) as used in Austria (region)
|
||||
::
|
||||
@@ -125,65 +149,6 @@ const { locale } = useI18n()
|
||||
|
||||
::
|
||||
|
||||
### Supported languages
|
||||
## Supported languages
|
||||
|
||||
:supported-languages
|
||||
|
||||
## Direction
|
||||
|
||||
Use the `dir` prop with `ltr` or `rtl` to set the global reading direction of your app:
|
||||
|
||||
```vue [app.vue]
|
||||
<template>
|
||||
<UApp dir="rtl">
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Dynamic direction
|
||||
|
||||
To dynamically change the global reading direction of your app, you can use VueUse's [useTextDirection](https://vueuse.org/core/useTextDirection/) composable to detect and switch between LTR and RTL text directions.
|
||||
|
||||
::steps{level="4"}
|
||||
|
||||
#### Install the `@vueuse/core` package
|
||||
|
||||
::code-group{sync="pm"}
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @vueuse/core
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add @vueuse/core
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install @vueuse/core
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @vueuse/core
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
#### Set the `dir` prop using `useTextDirection`
|
||||
|
||||
```vue [app.vue]
|
||||
<script setup lang="ts">
|
||||
import { useTextDirection } from '@vueuse/core'
|
||||
|
||||
const textDirection = useTextDirection({ initialValue: 'ltr' })
|
||||
const dir = computed(() => textDirection.value === 'rtl' ? 'rtl' : 'ltr')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp :dir="dir">
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
@@ -12,11 +12,13 @@ select:
|
||||
to: /getting-started/i18n/vue
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
::note{to="/components/app"}
|
||||
Nuxt UI provides an [App](/components/app) component that wraps your app to provide global configurations.
|
||||
::
|
||||
|
||||
## Locale
|
||||
### Locale
|
||||
|
||||
Use the `locale` prop with the locale you want to use from `@nuxt/ui/locale`:
|
||||
|
||||
@@ -32,6 +34,24 @@ import { fr } from '@nuxt/ui/locale'
|
||||
</template>
|
||||
```
|
||||
|
||||
### Direction
|
||||
|
||||
Each locale has a default direction, but you can override it using the `dir` prop if needed.
|
||||
|
||||
Use the `dir` prop with `ltr` or `rtl` to set the global reading direction of your app:
|
||||
|
||||
```vue [App.vue]
|
||||
<script setup lang="ts">
|
||||
import { fr } from '@nuxt/ui/locale'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp dir="rtl" :locale="fr">
|
||||
<RouterView />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Custom locale
|
||||
|
||||
You also have the option to add your locale using `defineLocale`:
|
||||
@@ -40,8 +60,12 @@ You also have the option to add your locale using `defineLocale`:
|
||||
<script setup lang="ts">
|
||||
import { defineLocale } from '@nuxt/ui/runtime/composables/defineLocale'
|
||||
|
||||
const locale = defineLocale('My custom locale', 'en', {
|
||||
// implement pairs
|
||||
const locale = defineLocale({
|
||||
name: 'My custom locale',
|
||||
code: 'en',
|
||||
messages: {
|
||||
// implement pairs
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -53,7 +77,7 @@ const locale = defineLocale('My custom locale', 'en', {
|
||||
```
|
||||
|
||||
::tip
|
||||
Look at the second parameter, there you need to pass the iso code of the language. Example:
|
||||
Look at the `code` parameter, there you need to pass the iso code of the language. Example:
|
||||
* `hi` Hindi (language)
|
||||
* `de-AT`: German (language) as used in Austria (region)
|
||||
::
|
||||
@@ -138,61 +162,3 @@ const { locale } = useI18n()
|
||||
## Supported languages
|
||||
|
||||
:supported-languages
|
||||
|
||||
## Direction
|
||||
|
||||
Use the `dir` prop with `ltr` or `rtl` to set the global reading direction of your app:
|
||||
|
||||
```vue [App.vue]
|
||||
<template>
|
||||
<UApp dir="rtl">
|
||||
<NuxtPage />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Dynamic direction
|
||||
|
||||
To dynamically change the global reading direction of your app, you can use VueUse's [useTextDirection](https://vueuse.org/core/useTextDirection/) composable to detect and switch between LTR and RTL text directions.
|
||||
|
||||
::steps{level="4"}
|
||||
|
||||
#### Install the `@vueuse/core` package
|
||||
|
||||
::code-group{sync="pm"}
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add @vueuse/core
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add @vueuse/core
|
||||
```
|
||||
|
||||
```bash [npm]
|
||||
npm install @vueuse/core
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun add @vueuse/core
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
#### Set the `dir` prop using `useTextDirection`
|
||||
|
||||
```vue [App.vue]
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useTextDirection } from '@vueuse/core'
|
||||
|
||||
const textDirection = useTextDirection()
|
||||
const dir = computed(() => textDirection.value === 'rtl' ? 'rtl' : 'ltr')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UApp :dir="dir">
|
||||
<RouterView />
|
||||
</UApp>
|
||||
</template>
|
||||
```
|
||||
|
||||
@@ -125,7 +125,7 @@ const ui = computed(() => alert({
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="link"
|
||||
:aria-label="t('ui.alert.close')"
|
||||
:aria-label="t('alert.close')"
|
||||
v-bind="typeof close === 'object' ? close : undefined"
|
||||
:class="ui.close({ class: props.ui?.close })"
|
||||
@click="emits('update:open', false)"
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { ConfigProviderProps, TooltipProviderProps } from 'radix-vue'
|
||||
import { localeContextInjectionKey } from '../composables/useLocale'
|
||||
import { extendDevtoolsMeta } from '../composables/extendDevtoolsMeta'
|
||||
import type { ToasterProps, Locale } from '../types'
|
||||
import { en } from '../locale'
|
||||
|
||||
export interface AppProps extends Omit<ConfigProviderProps, 'useId'> {
|
||||
tooltip?: TooltipProviderProps
|
||||
@@ -32,15 +33,16 @@ import USlideoverProvider from './SlideoverProvider.vue'
|
||||
const props = defineProps<AppProps>()
|
||||
defineSlots<AppSlots>()
|
||||
|
||||
const configProviderProps = useForwardProps(reactivePick(props, 'dir', 'scrollBody'))
|
||||
const configProviderProps = useForwardProps(reactivePick(props, 'scrollBody'))
|
||||
const tooltipProps = toRef(() => props.tooltip)
|
||||
const toasterProps = toRef(() => props.toaster)
|
||||
|
||||
provide(localeContextInjectionKey, computed(() => props.locale))
|
||||
const locale = computed(() => props.locale || en)
|
||||
provide(localeContextInjectionKey, locale)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ConfigProvider :use-id="() => (useId() as string)" v-bind="configProviderProps">
|
||||
<ConfigProvider :use-id="() => (useId() as string)" :dir="dir || locale.dir" v-bind="configProviderProps">
|
||||
<TooltipProvider v-bind="tooltipProps">
|
||||
<UToaster v-if="toaster !== null" v-bind="toasterProps">
|
||||
<slot />
|
||||
|
||||
@@ -281,7 +281,7 @@ defineExpose({
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:aria-label="t('ui.carousel.prev')"
|
||||
:aria-label="t('carousel.prev')"
|
||||
v-bind="typeof prev === 'object' ? prev : undefined"
|
||||
:class="ui.prev({ class: props.ui?.prev })"
|
||||
@click="scrollPrev"
|
||||
@@ -292,7 +292,7 @@ defineExpose({
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
:aria-label="t('ui.carousel.next')"
|
||||
:aria-label="t('carousel.next')"
|
||||
v-bind="typeof next === 'object' ? next : undefined"
|
||||
:class="ui.next({ class: props.ui?.next })"
|
||||
@click="scrollNext"
|
||||
@@ -302,7 +302,7 @@ defineExpose({
|
||||
<div v-if="dots" :class="ui.dots({ class: props.ui?.dots })">
|
||||
<template v-for="(_, index) in scrollSnaps" :key="index">
|
||||
<button
|
||||
:aria-label="t('ui.carousel.goto', { slide: index + 1 })"
|
||||
:aria-label="t('carousel.goto', { slide: index + 1 })"
|
||||
:class="ui.dot({ class: props.ui?.dot, active: selectedIndex === index })"
|
||||
@click="scrollTo(index)"
|
||||
/>
|
||||
|
||||
@@ -247,7 +247,7 @@ const groups = computed(() => {
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
:aria-label="t('ui.commandPalette.close')"
|
||||
:aria-label="t('commandPalette.close')"
|
||||
v-bind="typeof close === 'object' ? close : undefined"
|
||||
:class="ui.close({ class: props.ui?.close })"
|
||||
@click="emits('update:open', false)"
|
||||
@@ -261,7 +261,7 @@ const groups = computed(() => {
|
||||
<ComboboxContent :class="ui.content({ class: props.ui?.content })" :dismissable="false">
|
||||
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
|
||||
<slot name="empty" :search-term="searchTerm">
|
||||
{{ searchTerm ? t('ui.commandPalette.noMatch', { searchTerm }) : t('ui.commandPalette.noData') }}
|
||||
{{ searchTerm ? t('commandPalette.noMatch', { searchTerm }) : t('commandPalette.noData') }}
|
||||
</slot>
|
||||
</ComboboxEmpty>
|
||||
|
||||
|
||||
@@ -322,7 +322,7 @@ defineExpose({
|
||||
>
|
||||
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
||||
<slot name="create-item-label" :item="(creatable.item as T)">
|
||||
{{ t('ui.inputMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
|
||||
{{ t('inputMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
|
||||
</slot>
|
||||
</span>
|
||||
</ComboboxItem>
|
||||
@@ -412,7 +412,7 @@ defineExpose({
|
||||
<ComboboxContent :class="ui.content({ class: props.ui?.content })" v-bind="contentProps">
|
||||
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
|
||||
<slot name="empty" :search-term="searchTerm">
|
||||
{{ searchTerm ? t('ui.inputMenu.noMatch', { searchTerm }) : t('ui.inputMenu.noData') }}
|
||||
{{ searchTerm ? t('inputMenu.noMatch', { searchTerm }) : t('inputMenu.noData') }}
|
||||
</slot>
|
||||
</ComboboxEmpty>
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ const ui = computed(() => modal({
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
:aria-label="t('ui.modal.close')"
|
||||
:aria-label="t('modal.close')"
|
||||
v-bind="typeof close === 'object' ? close : undefined"
|
||||
:class="ui.close({ class: props.ui?.close })"
|
||||
/>
|
||||
|
||||
@@ -292,7 +292,7 @@ function onUpdateOpen(value: boolean) {
|
||||
>
|
||||
<span :class="ui.itemLabel({ class: props.ui?.itemLabel })">
|
||||
<slot name="create-item-label" :item="(creatable.item as T)">
|
||||
{{ t('ui.selectMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
|
||||
{{ t('selectMenu.create', { label: typeof creatable.item === 'object' ? get(creatable.item, props.labelKey as string) : creatable.item }) }}
|
||||
</slot>
|
||||
</span>
|
||||
</ComboboxItem>
|
||||
@@ -349,7 +349,7 @@ function onUpdateOpen(value: boolean) {
|
||||
|
||||
<ComboboxEmpty :class="ui.empty({ class: props.ui?.empty })">
|
||||
<slot name="empty" :search-term="searchTerm">
|
||||
{{ searchTerm ? t('ui.selectMenu.noMatch', { searchTerm }) : t('ui.selectMenu.noData') }}
|
||||
{{ searchTerm ? t('selectMenu.noMatch', { searchTerm }) : t('selectMenu.noData') }}
|
||||
</slot>
|
||||
</ComboboxEmpty>
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ const ui = computed(() => slideover({
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
:aria-label="t('ui.slideover.close')"
|
||||
:aria-label="t('slideover.close')"
|
||||
v-bind="typeof close === 'object' ? close : undefined"
|
||||
:class="ui.close({ class: props.ui?.close })"
|
||||
/>
|
||||
|
||||
@@ -241,7 +241,7 @@ defineExpose({
|
||||
<tr v-else :class="ui.tr({ class: [props.ui?.tr] })">
|
||||
<td :colspan="columns?.length" :class="ui.empty({ class: props.ui?.empty })">
|
||||
<slot name="empty">
|
||||
{{ t('ui.table.noData') }}
|
||||
{{ t('table.noData') }}
|
||||
</slot>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -153,7 +153,7 @@ defineExpose({
|
||||
size="md"
|
||||
color="neutral"
|
||||
variant="link"
|
||||
:aria-label="t('ui.toast.close')"
|
||||
:aria-label="t('toast.close')"
|
||||
v-bind="typeof close === 'object' ? close : undefined"
|
||||
:class="ui.close({ class: props.ui?.close })"
|
||||
@click.stop
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { Locale, LocalePair } from '../types/locale'
|
||||
import type { Locale, Direction, Messages } from '../types/locale'
|
||||
import { defu } from 'defu'
|
||||
|
||||
export function defineLocale(name: string, code: string, pair: LocalePair): Locale {
|
||||
return {
|
||||
name,
|
||||
code,
|
||||
ui: pair
|
||||
}
|
||||
interface DefineLocaleOptions {
|
||||
name: string
|
||||
code: string
|
||||
dir?: Direction
|
||||
messages: Messages
|
||||
}
|
||||
|
||||
export function defineLocale(options: DefineLocaleOptions): Locale {
|
||||
return defu<DefineLocaleOptions, [{ dir: Direction }]>(options, { dir: 'ltr' })
|
||||
}
|
||||
|
||||
@@ -3,11 +3,17 @@ import type { InjectionKey, Ref } from 'vue'
|
||||
import type { Locale } from '../types/locale'
|
||||
import { buildLocaleContext } from '../utils/locale'
|
||||
import { en } from '../locale'
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
|
||||
export const localeContextInjectionKey: InjectionKey<Ref<Locale | undefined>> = Symbol('nuxt-ui.locale-context')
|
||||
|
||||
export const useLocale = (localeOverrides?: Ref<Locale | undefined>) => {
|
||||
const _useLocale = (localeOverrides?: Ref<Locale | undefined>) => {
|
||||
const locale = localeOverrides || inject(localeContextInjectionKey, ref())!
|
||||
|
||||
/**
|
||||
* If for some reason the developer does not use `UApp`, we get the language back just in case.
|
||||
*/
|
||||
return buildLocaleContext(computed(() => locale.value || en))
|
||||
}
|
||||
|
||||
export const useLocale = createSharedComposable(_useLocale)
|
||||
|
||||
@@ -1,39 +1,44 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('العربية', 'ar', {
|
||||
inputMenu: {
|
||||
noMatch: 'لا توجد نتائج مطابقة',
|
||||
noData: 'لا توجد بيانات',
|
||||
create: 'إنشاء "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'لا توجد نتائج مطابقة',
|
||||
noData: 'لا توجد بيانات',
|
||||
close: 'إغلاق'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'لا توجد نتائج مطابقة',
|
||||
noData: 'لا توجد بيانات',
|
||||
create: 'إنشاء "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'السابق',
|
||||
next: 'التالي',
|
||||
goto: 'الذهاب إلي شريحة {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
slideover: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
alert: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
table: {
|
||||
noData: 'لا توجد بيانات'
|
||||
export default defineLocale({
|
||||
name: 'العربية',
|
||||
code: 'ar',
|
||||
dir: 'rtl',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'لا توجد نتائج مطابقة',
|
||||
noData: 'لا توجد بيانات',
|
||||
create: 'إنشاء "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'لا توجد نتائج مطابقة',
|
||||
noData: 'لا توجد بيانات',
|
||||
close: 'إغلاق'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'لا توجد نتائج مطابقة',
|
||||
noData: 'لا توجد بيانات',
|
||||
create: 'إنشاء "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'السابق',
|
||||
next: 'التالي',
|
||||
goto: 'الذهاب إلي شريحة {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
slideover: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
alert: {
|
||||
close: 'إغلاق'
|
||||
},
|
||||
table: {
|
||||
noData: 'لا توجد بيانات'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('Čeština', 'cs', {
|
||||
inputMenu: {
|
||||
noMatch: 'Žádná shoda',
|
||||
noData: 'Žádná data',
|
||||
create: 'Vytvořit "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Žádná shoda',
|
||||
noData: 'Žádná data',
|
||||
close: 'Zavřít'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Žádná shoda',
|
||||
noData: 'Žádná data',
|
||||
create: 'Vytvořit "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Předchozí',
|
||||
next: 'Další',
|
||||
goto: 'Přejít na {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
alert: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
table: {
|
||||
noData: 'Žádná data'
|
||||
export default defineLocale({
|
||||
name: 'Čeština',
|
||||
code: 'cs',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'Žádná shoda',
|
||||
noData: 'Žádná data',
|
||||
create: 'Vytvořit "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Žádná shoda',
|
||||
noData: 'Žádná data',
|
||||
close: 'Zavřít'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Žádná shoda',
|
||||
noData: 'Žádná data',
|
||||
create: 'Vytvořit "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Předchozí',
|
||||
next: 'Další',
|
||||
goto: 'Přejít na {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
alert: {
|
||||
close: 'Zavřít'
|
||||
},
|
||||
table: {
|
||||
noData: 'Žádná data'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('Deutsch', 'de', {
|
||||
inputMenu: {
|
||||
noMatch: 'Nichts gefunden',
|
||||
noData: 'Keine Daten',
|
||||
create: 'Erstellen "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Nichts gefunden',
|
||||
noData: 'Keine Daten',
|
||||
close: 'Schließen'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Nichts gefunden',
|
||||
noData: 'Keine Daten',
|
||||
create: 'Erstellen "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Weiter',
|
||||
next: 'Zurück',
|
||||
goto: 'Gehe zu {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
alert: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
table: {
|
||||
noData: 'Keine Daten'
|
||||
export default defineLocale({
|
||||
name: 'Deutsch',
|
||||
code: 'de',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'Nichts gefunden',
|
||||
noData: 'Keine Daten',
|
||||
create: 'Erstellen "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Nichts gefunden',
|
||||
noData: 'Keine Daten',
|
||||
close: 'Schließen'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Nichts gefunden',
|
||||
noData: 'Keine Daten',
|
||||
create: 'Erstellen "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Weiter',
|
||||
next: 'Zurück',
|
||||
goto: 'Gehe zu {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
alert: {
|
||||
close: 'Schließen'
|
||||
},
|
||||
table: {
|
||||
noData: 'Keine Daten'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('English', 'en', {
|
||||
inputMenu: {
|
||||
noMatch: 'No matching data',
|
||||
noData: 'No data',
|
||||
create: 'Create "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'No matching data',
|
||||
noData: 'No data',
|
||||
close: 'Close'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'No matching data',
|
||||
noData: 'No data',
|
||||
create: 'Create "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Close'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Prev',
|
||||
next: 'Next',
|
||||
goto: 'Go to slide {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Close'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Close'
|
||||
},
|
||||
alert: {
|
||||
close: 'Close'
|
||||
},
|
||||
table: {
|
||||
noData: 'No data'
|
||||
export default defineLocale({
|
||||
name: 'English',
|
||||
code: 'en',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'No matching data',
|
||||
noData: 'No data',
|
||||
create: 'Create "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'No matching data',
|
||||
noData: 'No data',
|
||||
close: 'Close'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'No matching data',
|
||||
noData: 'No data',
|
||||
create: 'Create "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Close'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Prev',
|
||||
next: 'Next',
|
||||
goto: 'Go to slide {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Close'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Close'
|
||||
},
|
||||
alert: {
|
||||
close: 'Close'
|
||||
},
|
||||
table: {
|
||||
noData: 'No data'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('Français', 'fr', {
|
||||
inputMenu: {
|
||||
noMatch: 'Aucune donnée correspondante',
|
||||
noData: 'Aucune donnée',
|
||||
create: 'Créer "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Aucune donnée correspondante',
|
||||
noData: 'Aucune donnée',
|
||||
close: 'Fermer'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Aucune donnée correspondante',
|
||||
noData: 'Aucune donnée',
|
||||
create: 'Créer "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Précédent',
|
||||
next: 'Suivant',
|
||||
goto: 'Aller à {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
alert: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
table: {
|
||||
noData: 'Aucune donnée'
|
||||
export default defineLocale({
|
||||
name: 'Français',
|
||||
code: 'fr',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'Aucune donnée correspondante',
|
||||
noData: 'Aucune donnée',
|
||||
create: 'Créer "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Aucune donnée correspondante',
|
||||
noData: 'Aucune donnée',
|
||||
close: 'Fermer'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Aucune donnée correspondante',
|
||||
noData: 'Aucune donnée',
|
||||
create: 'Créer "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Précédent',
|
||||
next: 'Suivant',
|
||||
goto: 'Aller à {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
alert: {
|
||||
close: 'Fermer'
|
||||
},
|
||||
table: {
|
||||
noData: 'Aucune donnée'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('Italiano', 'it', {
|
||||
inputMenu: {
|
||||
noMatch: 'Nessun dato corrispondente',
|
||||
noData: 'Nessun dato',
|
||||
create: 'Crea "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Nessun dato corrispondente',
|
||||
noData: 'Nessun dato',
|
||||
close: 'Chiudi'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Nessun dato corrispondente',
|
||||
noData: 'Nessun dato',
|
||||
create: 'Crea "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Precedente',
|
||||
next: 'Successiva',
|
||||
goto: 'Vai alla slide {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
alert: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
table: {
|
||||
noData: 'Nessun dato'
|
||||
export default defineLocale({
|
||||
name: 'Italiano',
|
||||
code: 'it',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'Nessun dato corrispondente',
|
||||
noData: 'Nessun dato',
|
||||
create: 'Crea "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Nessun dato corrispondente',
|
||||
noData: 'Nessun dato',
|
||||
close: 'Chiudi'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Nessun dato corrispondente',
|
||||
noData: 'Nessun dato',
|
||||
create: 'Crea "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Precedente',
|
||||
next: 'Successiva',
|
||||
goto: 'Vai alla slide {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
alert: {
|
||||
close: 'Chiudi'
|
||||
},
|
||||
table: {
|
||||
noData: 'Nessun dato'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('Русский', 'ru', {
|
||||
inputMenu: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
noData: 'Нет данных',
|
||||
create: 'Создать "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
noData: 'Нет данных',
|
||||
close: 'Закрыть'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
noData: 'Нет данных',
|
||||
create: 'Создать "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Назад',
|
||||
next: 'Далее',
|
||||
goto: 'Перейти к {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
alert: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
table: {
|
||||
noData: 'Нет данных'
|
||||
export default defineLocale({
|
||||
name: 'Русский',
|
||||
code: 'ru',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
noData: 'Нет данных',
|
||||
create: 'Создать "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
noData: 'Нет данных',
|
||||
close: 'Закрыть'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
noData: 'Нет данных',
|
||||
create: 'Создать "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
carousel: {
|
||||
prev: 'Назад',
|
||||
next: 'Далее',
|
||||
goto: 'Перейти к {slide}'
|
||||
},
|
||||
modal: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
slideover: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
alert: {
|
||||
close: 'Закрыть'
|
||||
},
|
||||
table: {
|
||||
noData: 'Нет данных'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('简体中文', 'zh-Hans', {
|
||||
inputMenu: {
|
||||
noMatch: '没有匹配的数据',
|
||||
noData: '没有数据',
|
||||
create: '创建 "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: '没有匹配的数据',
|
||||
noData: '没有数据',
|
||||
close: '关闭'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: '没有匹配的数据',
|
||||
noData: '没有数据',
|
||||
create: '创建 "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: '关闭'
|
||||
},
|
||||
carousel: {
|
||||
prev: '上一页',
|
||||
next: '下一页',
|
||||
goto: '跳转到第 {slide} 页'
|
||||
},
|
||||
modal: {
|
||||
close: '关闭'
|
||||
},
|
||||
slideover: {
|
||||
close: '关闭'
|
||||
},
|
||||
alert: {
|
||||
close: '关闭'
|
||||
},
|
||||
table: {
|
||||
noData: '没有数据'
|
||||
export default defineLocale({
|
||||
name: '简体中文',
|
||||
code: 'zh-Hans',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: '没有匹配的数据',
|
||||
noData: '没有数据',
|
||||
create: '创建 "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: '没有匹配的数据',
|
||||
noData: '没有数据',
|
||||
close: '关闭'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: '没有匹配的数据',
|
||||
noData: '没有数据',
|
||||
create: '创建 "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: '关闭'
|
||||
},
|
||||
carousel: {
|
||||
prev: '上一页',
|
||||
next: '下一页',
|
||||
goto: '跳转到第 {slide} 页'
|
||||
},
|
||||
modal: {
|
||||
close: '关闭'
|
||||
},
|
||||
slideover: {
|
||||
close: '关闭'
|
||||
},
|
||||
alert: {
|
||||
close: '关闭'
|
||||
},
|
||||
table: {
|
||||
noData: '没有数据'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,39 +1,43 @@
|
||||
import { defineLocale } from '../composables/defineLocale'
|
||||
|
||||
export default defineLocale('繁体中文', 'zh-Hant', {
|
||||
inputMenu: {
|
||||
noMatch: '沒有匹配的資料',
|
||||
noData: '沒有資料',
|
||||
create: '創建 "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: '沒有匹配的資料',
|
||||
noData: '沒有資料',
|
||||
close: '關閉'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: '沒有匹配的資料',
|
||||
noData: '沒有資料',
|
||||
create: '創建 "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: '關閉'
|
||||
},
|
||||
carousel: {
|
||||
prev: '上一頁',
|
||||
next: '下一頁',
|
||||
goto: '跳轉到第 {slide} 頁'
|
||||
},
|
||||
modal: {
|
||||
close: '關閉'
|
||||
},
|
||||
slideover: {
|
||||
close: '關閉'
|
||||
},
|
||||
alert: {
|
||||
close: '關閉'
|
||||
},
|
||||
table: {
|
||||
noData: '沒有資料'
|
||||
export default defineLocale({
|
||||
name: '繁体中文',
|
||||
code: 'zh-Hant',
|
||||
messages: {
|
||||
inputMenu: {
|
||||
noMatch: '沒有匹配的資料',
|
||||
noData: '沒有資料',
|
||||
create: '創建 "{label}"'
|
||||
},
|
||||
commandPalette: {
|
||||
noMatch: '沒有匹配的資料',
|
||||
noData: '沒有資料',
|
||||
close: '關閉'
|
||||
},
|
||||
selectMenu: {
|
||||
noMatch: '沒有匹配的資料',
|
||||
noData: '沒有資料',
|
||||
create: '創建 "{label}"'
|
||||
},
|
||||
toast: {
|
||||
close: '關閉'
|
||||
},
|
||||
carousel: {
|
||||
prev: '上一頁',
|
||||
next: '下一頁',
|
||||
goto: '跳轉到第 {slide} 頁'
|
||||
},
|
||||
modal: {
|
||||
close: '關閉'
|
||||
},
|
||||
slideover: {
|
||||
close: '關閉'
|
||||
},
|
||||
alert: {
|
||||
close: '關閉'
|
||||
},
|
||||
table: {
|
||||
noData: '沒有資料'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type LocalePair = {
|
||||
export type Messages = {
|
||||
inputMenu: {
|
||||
noMatch: string
|
||||
noData: string
|
||||
@@ -36,8 +36,11 @@ export type LocalePair = {
|
||||
}
|
||||
}
|
||||
|
||||
export type Direction = 'ltr' | 'rtl'
|
||||
|
||||
export type Locale = {
|
||||
name: string
|
||||
code: string
|
||||
ui: LocalePair
|
||||
dir: Direction
|
||||
messages: Messages
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export type Translator = (path: string, option?: TranslatorOption) => string
|
||||
export type LocaleContext = {
|
||||
locale: Ref<Locale>
|
||||
lang: Ref<string>
|
||||
dir: Ref<string>
|
||||
code: Ref<string>
|
||||
t: Translator
|
||||
}
|
||||
@@ -18,7 +19,7 @@ export function buildTranslator(locale: MaybeRef<Locale>): Translator {
|
||||
}
|
||||
|
||||
export function translate(path: string, option: undefined | TranslatorOption, locale: Locale): string {
|
||||
const prop: string = get(locale, path, path)
|
||||
const prop: string = get(locale, `messages.${path}`, path)
|
||||
|
||||
return prop.replace(
|
||||
/\{(\w+)\}/g,
|
||||
@@ -29,11 +30,13 @@ export function translate(path: string, option: undefined | TranslatorOption, lo
|
||||
export function buildLocaleContext(locale: MaybeRef<Locale>): LocaleContext {
|
||||
const lang = computed(() => unref(locale).name)
|
||||
const code = computed(() => unref(locale).code)
|
||||
const dir = computed(() => unref(locale).dir)
|
||||
const localeRef = isRef(locale) ? locale : ref(locale)
|
||||
|
||||
return {
|
||||
lang,
|
||||
code,
|
||||
dir,
|
||||
locale: localeRef,
|
||||
t: buildTranslator(locale)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user