forked from unovue/radix-vue
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docs: adding docs, resolves unovue#719 * docs: minor change --------- Co-authored-by: zernonia <[email protected]>
- Loading branch information
Showing
2 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
--- | ||
title: Internationalization (RTL) | ||
description: Radix Primitives support both LTR/RTL directions. Learn more about how to integrate internationalization with Radix. | ||
--- | ||
|
||
# Internationalization & RTL | ||
|
||
<Description> | ||
Radix Primitives support both LTR/RTL directions. Learn more about how to integrate internationalization with Radix. | ||
</Description> | ||
|
||
## Multi-Direction Support | ||
|
||
### Introduction | ||
|
||
This documentation provides guidance on how to utilize multi-directional support in Radix Vue with SSR support. Radix Primitives rely on [`Floating UI`](https://floating-ui.com/) to position floating elements, which requires to be fed the current direction of the web app. | ||
|
||
Radix components are LTR by default, but you are in control of what direction (only LTR, RTL, or both) you want to support. This section provides best practices to easily support RTL direction. | ||
|
||
### RTL | ||
|
||
[`ConfigProvider`](/utilities/config-provider) is a wrapper component to provide global configurations, including the directionality of the web app. | ||
|
||
When creating localized apps that require right-to-left (RTL) reading direction, you need to wrap your application with the `ConfigProvider` component to ensure all of the primitives adjust their behavior based on the `dir` prop. | ||
|
||
To make all Radix primitives RTL, wrap your entire App in `ConfigProvider` and pass the `dir` prop with the value `rtl`. | ||
|
||
Add the following code to your `app.vue` or main layout component: | ||
|
||
```vue | ||
<script setup lang="ts"> | ||
import { ConfigProvider } from 'radix-vue' | ||
</script> | ||
<template> | ||
<ConfigProvider dir="rtl"> | ||
<slot /> | ||
</ConfigProvider> | ||
</template> | ||
``` | ||
|
||
All Radix components that are wrapped in the provider inherit the `dir` attribute. | ||
|
||
### Dynamic Direction | ||
|
||
To dynamically change the direction of Radix primitives, we could leverage the [`useTextDirection`](https://vueuse.org/core/useTextDirection/) composable and combine it with our `ConfigProvider`. | ||
|
||
But first, we need to install the [`@vueuse/core`](https://vueuse.org/) package. | ||
|
||
```bash | ||
npm install @vueuse/core | ||
``` | ||
|
||
Then in your root Vue file: | ||
|
||
```vue | ||
<script setup lang="ts"> | ||
import { ConfigProvider } from 'radix-vue' | ||
import { useTextDirection } from '@vueuse/core' | ||
const dir = useTextDirection() | ||
</script> | ||
<template> | ||
<ConfigProvider :dir="dir"> | ||
<slot /> | ||
</ConfigProvider> | ||
</template> | ||
``` | ||
|
||
To support SSR - when the server has no access to the `html` and its direction, set `initialValue` in `useTextDirection`. | ||
|
||
```vue{5} | ||
<script setup lang="ts"> | ||
import { ConfigProvider } from 'radix-vue' | ||
import { useTextDirection } from '@vueuse/core' | ||
const dir = useTextDirection({ initialValue: 'rtl' }) | ||
</script> | ||
<template> | ||
<ConfigProvider :dir="dir"> | ||
<slot /> | ||
</ConfigProvider> | ||
</template> | ||
``` | ||
|
||
`dir` is a [`Ref`](https://vuejs.org/api/reactivity-core.html#ref), and by changing the value of it to either "ltr" or "rtl", the `dir` attribute on the `html` tag changes as well. | ||
|
||
## Internationalization | ||
|
||
Some languages are written from LTR and others are written in RTL. In a multi-language web app, you need to configure directionality alongside the translations. This is a simplified guide on how to achieve that using `radix-vue` primitives. | ||
|
||
But first, let's install some required packages. | ||
|
||
### Dependencies | ||
|
||
We rely on [`VueI18n`](https://vue-i18n.intlify.dev/) to manage different translations we want to support. | ||
|
||
```bash | ||
npm install vue-i18n@9 | ||
``` | ||
|
||
Go ahead and add some translations for the word "hello" in different languages at `main.ts`. | ||
|
||
```ts{4-26,29} | ||
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') | ||
``` | ||
|
||
### Language Selector | ||
|
||
After setting the translations and adding the `vue-i18n` plugin, we need a language selector in your `app.vue`. By changing the language using this `radix` select primitive: | ||
1. The translations are reactive to the new language | ||
2. The direction of the web app is reactive to the new language | ||
|
||
```vue | ||
<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> | ||
``` |