国際化とRTL
複数方向サポート
はじめに
このドキュメントでは、SSRサポートを含むRadix Vueでの複数方向サポートの使用方法に関するガイダンスを提供します。Radix Primitivesは、フローティング要素を配置するためにFloating UI
に依存しており、ウェブアプリの現在の向きを入力する必要があります。
RadixコンポーネントはデフォルトでLTRですが、サポートする向き(LTRのみ、RTLのみ、または両方)を制御できます。このセクションでは、RTL方向を簡単にサポートするためのベストプラクティスを示します。
RTL
ConfigProvider
は、ウェブアプリの方向性を含むグローバル設定を提供するラッパーコンポーネントです。
右から左(RTL)の読み取り方向を必要とするローカライズされたアプリを作成する場合は、dir
プロップに基づいてすべてのプリミティブが動作を調整するように、ConfigProvider
コンポーネントでアプリケーションをラップする必要があります。
すべてのRadixプリミティブをRTLにするには、ConfigProvider
でアプリケーション全体をラップし、dir
プロップに値rtl
を渡します。
次のコードを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で記述されています。多言語ウェブアプリでは、翻訳とともに方向性を設定する必要があります。これは、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
セレクトプリミティブを使用して言語を変更することで
- 翻訳は新しい言語に反応します。
- ウェブアプリの方向は新しい言語に反応します。
<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>