国际化 & RTL
多方向支持
介绍
本文档提供了有关如何在 Radix Vue 中利用多方向支持(包括 SSR 支持)的指南。Radix Primitives 依赖于 Floating UI
来定位浮动元素,这需要提供当前 Web 应用程序的方向。
Radix 组件默认情况下是 LTR,但您可以控制要支持的方向(仅 LTR、RTL 或两者)。本节提供了轻松支持 RTL 方向的最佳实践。
RTL
ConfigProvider
是一个包装组件,用于提供全局配置,包括 Web 应用程序的方向。
当创建需要从右到左 (RTL) 阅读方向的本地化应用程序时,您需要使用 ConfigProvider
组件包装您的应用程序,以确保所有基元根据 dir
属性调整其行为。
要使所有 Radix 基元都为 RTL,请将整个应用程序包装在 ConfigProvider
中,并使用值 rtl
传递 dir
属性。
将以下代码添加到您的 app.vue
或主布局组件中
<script setup lang="ts">
import { ConfigProvider } from 'radix-vue'
</script>
<template>
<ConfigProvider dir="rtl">
<slot />
</ConfigProvider>
</template>
所有包装在提供者中的 Radix 组件都继承了 dir
属性。
动态方向
要动态更改 Radix 基元的方向,我们可以利用 useTextDirection
可组合函数,并将其与我们的 ConfigProvider
相结合。
但首先,我们需要安装 @vueuse/core
包。
$ npm add @vueuse/core
然后在您的根 Vue 文件中
<script setup lang="ts">
import { computed } from 'vue'
import { ConfigProvider } from 'radix-vue'
import { useTextDirection } from '@vueuse/core'
const textDirection = useTextDirection()
const dir = computed(() => textDirection.value === 'rtl' ? 'rtl' : 'ltr')
</script>
<template>
<ConfigProvider :dir="dir">
<slot />
</ConfigProvider>
</template>
要支持 SSR - 当服务器无法访问 html
及其方向时,请在 useTextDirection
中设置 initialValue
。
<script setup lang="ts">
import { ConfigProvider } from 'radix-vue'
import { useTextDirection } from '@vueuse/core'
const textDirection = useTextDirection({ initialValue: 'rtl' })
const dir = computed(() => textDirection.value === 'rtl' ? 'rtl' : 'ltr')
</script>
<template>
<ConfigProvider :dir="dir">
<slot />
</ConfigProvider>
</template>
信息
dir
属性不支持 auto
作为值,因此我们需要一个中间 Ref 来明确定义方向。
textDirection
是一个 Ref
,通过将其值更改为 "ltr" 或 "rtl",html
标签上的 dir
属性也会发生变化。
国际化
有些语言是从 LTR 写的,而另一些则是从 RTL 写的。在多语言 Web 应用程序中,您需要与翻译一起配置方向。这是一个关于如何使用 radix-vue
基元实现这一目标的简化指南。
但首先,让我们安装一些必需的包。
依赖项
我们依赖于 VueI18n
来管理我们想要支持的不同翻译。
$ npm add vue-i18n@9
继续在 main.ts
中为不同的语言添加 "hello" 这个词的一些翻译。
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createI18n } from 'vue-i18n'
const messages = {
en: {
hello: 'Hello',
},
fa: {
hello: 'درود',
},
ar: {
hello: 'مرحبا',
},
ja: {
hello: 'こんにちは',
}
}
const i18n = createI18n({
legacy: false, // you must set `false` to use the Composition API
locale: 'en', // set default locale
availableLocales: ['en', 'fa', 'ar', 'ja'],
messages,
})
createApp(App)
.use(i18n)
.mount('#app')
语言选择器
在设置翻译并添加 vue-i18n
插件后,我们需要在您的 app.vue
中添加一个语言选择器。通过使用此 radix
选择基元来更改语言
- 翻译对新语言有反应
- Web 应用程序的方向对新语言有反应
<script setup lang="ts">
import { ConfigProvider, SelectContent, SelectGroup, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectPortal, SelectRoot, SelectScrollDownButton, SelectScrollUpButton, SelectTrigger, SelectValue, SelectViewport, } from 'radix-vue'
import { useTextDirection } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { ref } from 'vue'
type LanguageInfo = {
label: string
value: string
dir: 'ltr' | 'rtl'
}
const dir = useTextDirection({ initialValue: 'ltr' })
const { locale } = useI18n()
const selectedLanguage = ref<string>()
const languages: LanguageInfo[] = [
{ label: 'English', value: 'en', dir: 'ltr' },
{ label: 'Persian', value: 'fa', dir: 'rtl' },
{ label: 'Arabic', value: 'ar', dir: 'rtl' },
{ label: 'Japanese', value: 'ja', dir: 'ltr' },
]
function selectLanguage(newLanguage: string) {
const langInfo = languages.find(item => item.value === newLanguage)
if (!langInfo)
return
dir.value = langInfo.dir
locale.value = langInfo.value
}
</script>
<template>
<ConfigProvider :dir="dir">
<div class="flex flex-col max-w-[1400px] mx-auto gap-y-[8rem] justify-center items-center p-10">
<div class="text-2xl">
👋 {{ $t("hello") }}
</div>
<div class="text-2xl">
HTML is in <span class="text-bold text-purple-500">{{ dir }}</span> mode
</div>
<SelectRoot
v-model="selectedLanguage"
@update:model-value="selectLanguage"
>
<SelectTrigger
class="inline-flex min-w-[160px] items-center justify-between rounded px-[15px] text-[13px] leading-none h-[35px] gap-[5px] bg-white text-grass11 shadow-[0_2px_10px] shadow-black/10 hover:bg-mauve3 focus:shadow-[0_0_0_2px] focus:shadow-black data-[placeholder]:text-green9 outline-none"
aria-label="Customize options"
>
<SelectValue placeholder="Select a language..." />
<Icon
icon="radix-icons:chevron-down"
class="h-3.5 w-3.5"
/>
</SelectTrigger>
<SelectPortal>
<SelectContent
class="min-w-[160px] bg-white rounded shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade z-[100]"
:side-offset="5"
>
<SelectScrollUpButton
class="flex items-center justify-center h-[25px] bg-white text-violet11 cursor-default"
>
<Icon icon="radix-icons:chevron-up" />
</SelectScrollUpButton>
<SelectViewport class="p-[5px]">
<SelectLabel class="px-[25px] text-xs leading-[25px] text-mauve11">
Languages
</SelectLabel>
<SelectGroup>
<SelectItem
v-for="(option, index) in languages"
:key="index"
class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-green9 data-[highlighted]:text-green1"
:value="option.value"
>
<SelectItemIndicator class="absolute left-0 w-[25px] inline-flex items-center justify-center">
<Icon icon="radix-icons:check" />
</SelectItemIndicator>
<SelectItemText>
{{ option.label }}
</SelectItemText>
</SelectItem>
</SelectGroup>
</SelectViewport>
<SelectScrollDownButton
class="flex items-center justify-center h-[25px] bg-white text-violet11 cursor-default"
>
<Icon icon="radix-icons:chevron-down" />
</SelectScrollDownButton>
</SelectContent>
</SelectPortal>
</SelectRoot>
</div>
</ConfigProvider>
</template>