@intlayer/docs 7.3.13 → 7.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blog/ar/intlayer_with_i18next.md +3 -0
- package/blog/ar/intlayer_with_next-i18next.md +3 -0
- package/blog/ar/intlayer_with_next-intl.md +3 -0
- package/blog/ar/intlayer_with_react-i18next.md +3 -0
- package/blog/ar/intlayer_with_react-intl.md +3 -0
- package/blog/ar/intlayer_with_vue-i18n.md +3 -0
- package/blog/de/intlayer_with_i18next.md +3 -0
- package/blog/de/intlayer_with_next-i18next.md +3 -0
- package/blog/de/intlayer_with_next-intl.md +3 -0
- package/blog/de/intlayer_with_react-i18next.md +3 -0
- package/blog/de/intlayer_with_react-intl.md +3 -0
- package/blog/de/intlayer_with_vue-i18n.md +3 -0
- package/blog/en/intlayer_with_i18next.md +7 -0
- package/blog/en/intlayer_with_next-i18next.md +3 -0
- package/blog/en/intlayer_with_next-intl.md +7 -0
- package/blog/en/intlayer_with_react-i18next.md +3 -0
- package/blog/en/intlayer_with_react-intl.md +3 -0
- package/blog/en/intlayer_with_vue-i18n.md +3 -0
- package/blog/en-GB/intlayer_with_i18next.md +3 -0
- package/blog/en-GB/intlayer_with_next-i18next.md +3 -0
- package/blog/en-GB/intlayer_with_next-intl.md +3 -0
- package/blog/en-GB/intlayer_with_react-i18next.md +3 -0
- package/blog/en-GB/intlayer_with_react-intl.md +3 -0
- package/blog/en-GB/intlayer_with_vue-i18n.md +3 -0
- package/blog/es/intlayer_with_i18next.md +3 -0
- package/blog/es/intlayer_with_next-i18next.md +3 -0
- package/blog/es/intlayer_with_next-intl.md +3 -0
- package/blog/es/intlayer_with_react-i18next.md +3 -0
- package/blog/es/intlayer_with_react-intl.md +3 -0
- package/blog/es/intlayer_with_vue-i18n.md +3 -0
- package/blog/fr/intlayer_with_i18next.md +3 -0
- package/blog/fr/intlayer_with_next-i18next.md +3 -0
- package/blog/fr/intlayer_with_next-intl.md +3 -0
- package/blog/fr/intlayer_with_react-i18next.md +3 -0
- package/blog/fr/intlayer_with_react-intl.md +3 -0
- package/blog/fr/intlayer_with_vue-i18n.md +3 -0
- package/blog/hi/intlayer_with_i18next.md +3 -0
- package/blog/hi/intlayer_with_next-i18next.md +3 -0
- package/blog/hi/intlayer_with_next-intl.md +3 -0
- package/blog/hi/intlayer_with_react-i18next.md +3 -0
- package/blog/hi/intlayer_with_react-intl.md +3 -0
- package/blog/hi/intlayer_with_vue-i18n.md +3 -0
- package/blog/id/intlayer_with_i18next.md +3 -0
- package/blog/id/intlayer_with_next-i18next.md +3 -0
- package/blog/id/intlayer_with_next-intl.md +3 -0
- package/blog/id/intlayer_with_react-i18next.md +3 -0
- package/blog/id/intlayer_with_react-intl.md +3 -0
- package/blog/id/intlayer_with_vue-i18n.md +3 -0
- package/blog/it/intlayer_with_i18next.md +3 -0
- package/blog/it/intlayer_with_next-i18next.md +3 -0
- package/blog/it/intlayer_with_next-intl.md +3 -0
- package/blog/it/intlayer_with_react-i18next.md +3 -0
- package/blog/it/intlayer_with_react-intl.md +3 -0
- package/blog/it/intlayer_with_vue-i18n.md +3 -0
- package/blog/ja/intlayer_with_i18next.md +3 -0
- package/blog/ja/intlayer_with_next-i18next.md +3 -0
- package/blog/ja/intlayer_with_next-intl.md +3 -0
- package/blog/ja/intlayer_with_react-i18next.md +3 -0
- package/blog/ja/intlayer_with_react-intl.md +3 -0
- package/blog/ja/intlayer_with_vue-i18n.md +3 -0
- package/blog/ko/intlayer_with_i18next.md +3 -0
- package/blog/ko/intlayer_with_next-i18next.md +3 -0
- package/blog/ko/intlayer_with_next-intl.md +3 -0
- package/blog/ko/intlayer_with_react-i18next.md +3 -0
- package/blog/ko/intlayer_with_react-intl.md +3 -0
- package/blog/ko/intlayer_with_vue-i18n.md +3 -0
- package/blog/pl/intlayer_with_i18next.md +3 -0
- package/blog/pl/intlayer_with_next-i18next.md +3 -0
- package/blog/pl/intlayer_with_next-intl.md +3 -0
- package/blog/pl/intlayer_with_react-i18next.md +3 -0
- package/blog/pl/intlayer_with_react-intl.md +3 -0
- package/blog/pl/intlayer_with_vue-i18n.md +3 -0
- package/blog/pt/intlayer_with_i18next.md +3 -0
- package/blog/pt/intlayer_with_next-i18next.md +3 -0
- package/blog/pt/intlayer_with_next-intl.md +3 -0
- package/blog/pt/intlayer_with_react-i18next.md +3 -0
- package/blog/pt/intlayer_with_react-intl.md +3 -0
- package/blog/pt/intlayer_with_vue-i18n.md +3 -0
- package/blog/ru/intlayer_with_i18next.md +3 -0
- package/blog/ru/intlayer_with_next-i18next.md +3 -0
- package/blog/ru/intlayer_with_next-intl.md +3 -0
- package/blog/ru/intlayer_with_react-i18next.md +3 -0
- package/blog/ru/intlayer_with_react-intl.md +3 -0
- package/blog/ru/intlayer_with_vue-i18n.md +3 -0
- package/blog/tr/intlayer_with_i18next.md +3 -0
- package/blog/tr/intlayer_with_next-i18next.md +3 -0
- package/blog/tr/intlayer_with_next-intl.md +3 -0
- package/blog/tr/intlayer_with_react-i18next.md +3 -0
- package/blog/tr/intlayer_with_vue-i18n.md +3 -0
- package/blog/vi/intlayer_with_i18next.md +3 -0
- package/blog/vi/intlayer_with_next-i18next.md +3 -0
- package/blog/vi/intlayer_with_next-intl.md +3 -0
- package/blog/vi/intlayer_with_react-i18next.md +3 -0
- package/blog/vi/intlayer_with_react-intl.md +3 -0
- package/blog/vi/intlayer_with_vue-i18n.md +3 -0
- package/blog/zh/intlayer_with_i18next.md +3 -0
- package/blog/zh/intlayer_with_next-i18next.md +3 -0
- package/blog/zh/intlayer_with_next-intl.md +3 -0
- package/blog/zh/intlayer_with_react-i18next.md +3 -0
- package/blog/zh/intlayer_with_react-intl.md +3 -0
- package/blog/zh/intlayer_with_vue-i18n.md +3 -0
- package/docs/ar/intlayer_with_lynx+react.md +1 -1
- package/docs/ar/intlayer_with_vite+react.md +99 -331
- package/docs/ar/plugins/sync-json.md +3 -0
- package/docs/de/intlayer_with_lynx+react.md +1 -1
- package/docs/de/intlayer_with_vite+react.md +116 -380
- package/docs/de/plugins/sync-json.md +3 -0
- package/docs/en/intlayer_with_vite+react.md +6 -10
- package/docs/en/plugins/sync-json.md +3 -0
- package/docs/en-GB/intlayer_with_vite+react.md +62 -74
- package/docs/en-GB/plugins/sync-json.md +3 -0
- package/docs/es/intlayer_with_vite+react.md +101 -333
- package/docs/es/plugins/sync-json.md +3 -0
- package/docs/fr/intlayer_with_vite+react.md +101 -357
- package/docs/fr/plugins/sync-json.md +3 -0
- package/docs/hi/intlayer_with_vite+react.md +120 -333
- package/docs/hi/plugins/sync-json.md +3 -0
- package/docs/id/intlayer_with_vite+react.md +7 -13
- package/docs/id/plugins/sync-json.md +3 -0
- package/docs/it/intlayer_with_lynx+react.md +1 -1
- package/docs/it/intlayer_with_vite+react.md +121 -393
- package/docs/it/plugins/sync-json.md +3 -0
- package/docs/ja/intlayer_with_vite+react.md +106 -378
- package/docs/ja/plugins/sync-json.md +3 -0
- package/docs/ko/intlayer_with_lynx+react.md +1 -1
- package/docs/ko/intlayer_with_vite+react.md +90 -322
- package/docs/ko/plugins/sync-json.md +3 -0
- package/docs/pl/intlayer_with_vite+react.md +25 -21
- package/docs/pl/plugins/sync-json.md +3 -0
- package/docs/pt/intlayer_with_vite+react.md +96 -328
- package/docs/pt/plugins/sync-json.md +3 -0
- package/docs/ru/intlayer_with_lynx+react.md +1 -1
- package/docs/ru/intlayer_with_vite+react.md +109 -362
- package/docs/ru/plugins/sync-json.md +3 -0
- package/docs/tr/intlayer_with_vite+react.md +132 -366
- package/docs/tr/plugins/sync-json.md +3 -0
- package/docs/vi/intlayer_with_vite+react.md +16 -19
- package/docs/vi/plugins/sync-json.md +3 -0
- package/docs/zh/intlayer_with_tanstack.md +1 -1
- package/docs/zh/intlayer_with_vite+react.md +91 -374
- package/docs/zh/plugins/sync-json.md +3 -0
- package/frequent_questions/ar/customized_locale_list.md +1 -1
- package/frequent_questions/de/customized_locale_list.md +1 -1
- package/frequent_questions/en/customized_locale_list.md +1 -1
- package/frequent_questions/en-GB/customized_locale_list.md +1 -1
- package/frequent_questions/es/customized_locale_list.md +1 -1
- package/frequent_questions/fr/customized_locale_list.md +1 -1
- package/frequent_questions/hi/customized_locale_list.md +1 -1
- package/frequent_questions/id/customized_locale_list.md +1 -1
- package/frequent_questions/it/customized_locale_list.md +1 -1
- package/frequent_questions/ja/customized_locale_list.md +1 -1
- package/frequent_questions/ko/customized_locale_list.md +1 -1
- package/frequent_questions/pl/customized_locale_list.md +1 -1
- package/frequent_questions/pt/customized_locale_list.md +1 -1
- package/frequent_questions/ru/customized_locale_list.md +1 -1
- package/frequent_questions/tr/customized_locale_list.md +1 -1
- package/frequent_questions/vi/customized_locale_list.md +1 -1
- package/frequent_questions/zh/customized_locale_list.md +1 -1
- package/package.json +10 -9
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
createdAt: 2024-03-07
|
|
3
|
-
updatedAt:
|
|
4
|
-
title: Como traduzir
|
|
3
|
+
updatedAt: 2025-12-10
|
|
4
|
+
title: Como traduzir sua aplicação Vite e React – guia i18n 2025
|
|
5
5
|
description: Aprenda como adicionar internacionalização (i18n) à sua aplicação Vite e React usando Intlayer. Siga este guia para tornar seu app multilíngue.
|
|
6
6
|
keywords:
|
|
7
7
|
- Internacionalização
|
|
@@ -16,23 +16,19 @@ slugs:
|
|
|
16
16
|
- environment
|
|
17
17
|
- vite-and-react
|
|
18
18
|
applicationTemplate: https://github.com/aymericzip/intlayer-vite-react-template
|
|
19
|
-
youtubeVideo: https://www.youtube.com/watch?v=dS9L7uJeak4
|
|
19
|
+
youtubeVideo: https://www.youtube.com/watch?v=dS9L7uJeak4
|
|
20
20
|
history:
|
|
21
21
|
- version: 5.5.10
|
|
22
22
|
date: 2025-06-29
|
|
23
23
|
changes: Histórico inicial
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
#
|
|
26
|
+
# Traduza seu site Vite e React usando Intlayer | Internacionalização (i18n)
|
|
27
27
|
|
|
28
28
|
## Índice
|
|
29
29
|
|
|
30
30
|
<TOC/>
|
|
31
31
|
|
|
32
|
-
<iframe title="The best i18n solution for Vite and React? Discover Intlayer" class="m-auto aspect-[16/9] w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/dS9L7uJeak4?si=VaKmrYMmXjo3xpk2"/>
|
|
33
|
-
|
|
34
|
-
Veja o [Modelo de Aplicação](https://github.com/aymericzip/intlayer-vite-react-template) no GitHub.
|
|
35
|
-
|
|
36
32
|
## O que é Intlayer?
|
|
37
33
|
|
|
38
34
|
**Intlayer** é uma biblioteca inovadora e open-source de internacionalização (i18n) projetada para simplificar o suporte multilíngue em aplicações web modernas.
|
|
@@ -48,6 +44,27 @@ Com o Intlayer, você pode:
|
|
|
48
44
|
|
|
49
45
|
## Guia Passo a Passo para Configurar o Intlayer em uma Aplicação Vite e React
|
|
50
46
|
|
|
47
|
+
<Tab defaultTab="video">
|
|
48
|
+
<TabItem label="Video" value="video">
|
|
49
|
+
|
|
50
|
+
<iframe title="The best i18n solution for Vite and React? Discover Intlayer" class="m-auto aspect-[16/9] w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/dS9L7uJeak4?si=VaKmrYMmXjo3xpk2"/>
|
|
51
|
+
|
|
52
|
+
</TabItem>
|
|
53
|
+
<TabItem label="Code" value="code">
|
|
54
|
+
|
|
55
|
+
<iframe
|
|
56
|
+
src="https://stackblitz.com/github/aymericzip/intlayer-vite-react-template?embed=1&ctl=1&file=intlayer.config.ts"
|
|
57
|
+
className="m-auto overflow-hidden rounded-lg border-0 max-md:size-full max-md:h-[700px] md:aspect-16/9 md:w-full"
|
|
58
|
+
title="Demo CodeSandbox - How to Internationalize your application using Intlayer"
|
|
59
|
+
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
|
|
60
|
+
loading="lazy"
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
</TabItem>
|
|
64
|
+
</Tab>
|
|
65
|
+
|
|
66
|
+
Veja o [Modelo de Aplicação](https://github.com/aymericzip/intlayer-vite-react-template) no GitHub.
|
|
67
|
+
|
|
51
68
|
### Passo 1: Instalar Dependências
|
|
52
69
|
|
|
53
70
|
Instale os pacotes necessários usando npm:
|
|
@@ -68,14 +85,13 @@ yarn add vite-intlayer --save-dev
|
|
|
68
85
|
```
|
|
69
86
|
|
|
70
87
|
- **intlayer**
|
|
71
|
-
|
|
72
|
-
O pacote principal que fornece ferramentas de internacionalização para gerenciamento de configuração, tradução, [declaração de conteúdo](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/dictionary/get_started.md), transpiração e [comandos CLI](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/intlayer_cli.md).
|
|
88
|
+
O pacote principal que fornece ferramentas de internacionalização para gerenciamento de configuração, tradução, [declaração de conteúdo](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/dictionary/content_file.md), transpiração e [comandos CLI](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/cli/index.md).
|
|
73
89
|
|
|
74
90
|
- **react-intlayer**
|
|
75
91
|
O pacote que integra o Intlayer com aplicações React. Ele fornece provedores de contexto e hooks para internacionalização em React.
|
|
76
92
|
|
|
77
93
|
- **vite-intlayer**
|
|
78
|
-
Inclui o plugin Vite para integrar o Intlayer com o [empacotador Vite](https://vite.dev/guide/why.html#why-bundle-for-production), além de middleware para detectar a localidade preferida do
|
|
94
|
+
Inclui o plugin Vite para integrar o Intlayer com o [empacotador Vite](https://vite.dev/guide/why.html#why-bundle-for-production), além de middleware para detectar a localidade preferida do utilizador, gerir cookies e tratar o redirecionamento de URL.
|
|
79
95
|
|
|
80
96
|
### Passo 2: Configuração do seu projeto
|
|
81
97
|
|
|
@@ -137,7 +153,7 @@ const config = {
|
|
|
137
153
|
module.exports = config;
|
|
138
154
|
```
|
|
139
155
|
|
|
140
|
-
> Através deste arquivo de configuração, você pode configurar URLs localizadas, redirecionamento de middleware, nomes de cookies, a localização e extensão das suas declarações de conteúdo, desabilitar logs do Intlayer no console e muito mais. Para uma lista completa dos parâmetros disponíveis, consulte a [documentação de configuração](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
156
|
+
> Através deste arquivo de configuração, você pode configurar URLs localizadas, redirecionamento de middleware, nomes de cookies, a localização e extensão das suas declarações de conteúdo, desabilitar logs do Intlayer no console e muito mais. Para uma lista completa dos parâmetros disponíveis, consulte a [documentação de configuração](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/configuration.md).
|
|
141
157
|
|
|
142
158
|
### Passo 3: Integre o Intlayer na sua Configuração do Vite
|
|
143
159
|
|
|
@@ -429,7 +445,7 @@ module.exports = appContent;
|
|
|
429
445
|
|
|
430
446
|
> Suas declarações de conteúdo podem ser definidas em qualquer lugar da sua aplicação assim que forem incluídas no diretório `contentDir` (por padrão, `./src`). E devem corresponder à extensão do arquivo de declaração de conteúdo (por padrão, `.content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}`).
|
|
431
447
|
|
|
432
|
-
> Para mais detalhes, consulte a [documentação de declaração de conteúdo](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
448
|
+
> Para mais detalhes, consulte a [documentação de declaração de conteúdo](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/dictionary/content_file.md).
|
|
433
449
|
|
|
434
450
|
> Se seu arquivo de conteúdo incluir código TSX, você deve considerar importar `import React from "react";` no seu arquivo de conteúdo.
|
|
435
451
|
|
|
@@ -649,87 +665,15 @@ Exemplo:
|
|
|
649
665
|
- https://example.com/fr/about
|
|
650
666
|
```
|
|
651
667
|
|
|
652
|
-
> Por padrão, as rotas não são prefixadas para o idioma padrão. Se você quiser prefixar o idioma padrão, pode definir a opção `middleware.prefixDefault` como `true` na sua configuração. Veja a [documentação de configuração](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
668
|
+
> Por padrão, as rotas não são prefixadas para o idioma padrão. Se você quiser prefixar o idioma padrão, pode definir a opção `middleware.prefixDefault` como `true` na sua configuração. Veja a [documentação de configuração](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/configuration.md) para mais informações.
|
|
653
669
|
|
|
654
670
|
Para adicionar roteamento localizado à sua aplicação, você pode criar um componente `LocaleRouter` que envolve as rotas da sua aplicação e gerencia o roteamento baseado no idioma. Aqui está um exemplo usando [React Router](https://reactrouter.com/home):
|
|
655
671
|
|
|
656
672
|
```tsx fileName="src/components/LocaleRouter.tsx" codeFormat="typescript"
|
|
657
|
-
//
|
|
658
|
-
import { type Locales, configuration, getPathWithoutLocale } from "intlayer"; // Funções utilitárias e tipos do 'intlayer'
|
|
673
|
+
import { localeMap } from "intlayer"; // Funções utilitárias e tipos do 'intlayer'
|
|
659
674
|
import type { FC, PropsWithChildren } from "react"; // Tipos React para componentes funcionais e props
|
|
660
675
|
import { IntlayerProvider } from "react-intlayer"; // Provedor para contexto de internacionalização
|
|
661
|
-
import {
|
|
662
|
-
BrowserRouter,
|
|
663
|
-
Routes,
|
|
664
|
-
Route,
|
|
665
|
-
Navigate,
|
|
666
|
-
useLocation,
|
|
667
|
-
} from "react-router-dom"; // Componentes do roteador para gerenciar navegação
|
|
668
|
-
|
|
669
|
-
// Desestruturando configuração do Intlayer
|
|
670
|
-
const { internationalization, middleware } = configuration;
|
|
671
|
-
const { locales, defaultLocale } = internationalization;
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* Um componente que gerencia a localização e envolve os filhos com o contexto de local apropriado.
|
|
675
|
-
* Ele gerencia a detecção e validação da localidade baseada na URL.
|
|
676
|
-
*/
|
|
677
|
-
const AppLocalized: FC<PropsWithChildren<{ locale: Locales }>> = ({
|
|
678
|
-
children,
|
|
679
|
-
locale,
|
|
680
|
-
}) => {
|
|
681
|
-
const { pathname, search } = useLocation(); // Obtém o caminho atual da URL
|
|
682
|
-
|
|
683
|
-
// Determina o idioma atual, usando o padrão caso não seja fornecido
|
|
684
|
-
const currentLocale = locale ?? defaultLocale;
|
|
685
|
-
|
|
686
|
-
// Remove o prefixo do idioma do caminho para construir um caminho base
|
|
687
|
-
const pathWithoutLocale = getPathWithoutLocale(
|
|
688
|
-
pathname // Caminho atual da URL
|
|
689
|
-
);
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Se middleware.prefixDefault for true, o idioma padrão deve sempre ser prefixado.
|
|
693
|
-
*/
|
|
694
|
-
if (middleware.prefixDefault) {
|
|
695
|
-
// Valida o idioma
|
|
696
|
-
if (!locale || !locales.includes(locale)) {
|
|
697
|
-
// Redireciona para o idioma padrão com o caminho atualizado
|
|
698
|
-
return (
|
|
699
|
-
<Navigate
|
|
700
|
-
to={`/${defaultLocale}/${pathWithoutLocale}${search}`}
|
|
701
|
-
replace // Substitui a entrada atual do histórico pela nova
|
|
702
|
-
/>
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Envolve os filhos com o IntlayerProvider e define a localidade atual
|
|
707
|
-
return (
|
|
708
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
709
|
-
);
|
|
710
|
-
} else {
|
|
711
|
-
/**
|
|
712
|
-
* Quando middleware.prefixDefault é falso, a localidade padrão não é prefixada.
|
|
713
|
-
* Assegura que a localidade atual seja válida e não a localidade padrão.
|
|
714
|
-
*/
|
|
715
|
-
if (
|
|
716
|
-
currentLocale.toString() !== defaultLocale.toString() &&
|
|
717
|
-
!locales
|
|
718
|
-
.filter(
|
|
719
|
-
(locale) => locale.toString() !== defaultLocale.toString() // Exclui a localidade padrão
|
|
720
|
-
)
|
|
721
|
-
.includes(currentLocale) // Verifica se o idioma atual está na lista de idiomas válidos
|
|
722
|
-
) {
|
|
723
|
-
// Redireciona para o caminho sem o prefixo do idioma
|
|
724
|
-
return <Navigate to={`${pathWithoutLocale}${search}`} replace />;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// Envolve os filhos com o IntlayerProvider e define o idioma atual
|
|
728
|
-
return (
|
|
729
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
730
|
-
);
|
|
731
|
-
}
|
|
732
|
-
};
|
|
676
|
+
import { BrowserRouter, Route, Routes } from "react-router-dom"; // Componentes do roteador para gerenciar navegação
|
|
733
677
|
|
|
734
678
|
/**
|
|
735
679
|
* Um componente de roteador que configura rotas específicas por idioma.
|
|
@@ -738,256 +682,81 @@ const AppLocalized: FC<PropsWithChildren<{ locale: Locales }>> = ({
|
|
|
738
682
|
export const LocaleRouter: FC<PropsWithChildren> = ({ children }) => (
|
|
739
683
|
<BrowserRouter>
|
|
740
684
|
<Routes>
|
|
741
|
-
{
|
|
742
|
-
|
|
743
|
-
(
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
/>
|
|
752
|
-
))}
|
|
753
|
-
|
|
754
|
-
{
|
|
755
|
-
// Se o prefixo da localidade padrão estiver desativado, renderiza os filhos diretamente na raiz
|
|
756
|
-
!middleware.prefixDefault && (
|
|
757
|
-
<Route
|
|
758
|
-
path="*"
|
|
759
|
-
element={
|
|
760
|
-
<AppLocalized locale={defaultLocale}>{children}</AppLocalized>
|
|
761
|
-
} // Envolve os filhos com o gerenciamento de localidade
|
|
762
|
-
/>
|
|
763
|
-
)
|
|
764
|
-
}
|
|
685
|
+
{localeMap(({ locale, urlPrefix }) => (
|
|
686
|
+
<Route
|
|
687
|
+
// Padrão de rota para capturar o idioma (ex: /en/, /fr/) e corresponder a todos os caminhos subsequentes
|
|
688
|
+
path={`${urlPrefix}/*`}
|
|
689
|
+
key={locale}
|
|
690
|
+
element={
|
|
691
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
692
|
+
} // Envolve os filhos com o gerenciamento de idioma
|
|
693
|
+
/>
|
|
694
|
+
))}
|
|
765
695
|
</Routes>
|
|
766
696
|
</BrowserRouter>
|
|
767
697
|
);
|
|
768
698
|
```
|
|
769
699
|
|
|
770
700
|
```jsx fileName="src/components/LocaleRouter.mjx" codeFormat="esm"
|
|
771
|
-
//
|
|
772
|
-
import {
|
|
773
|
-
|
|
774
|
-
import {
|
|
775
|
-
import {
|
|
776
|
-
BrowserRouter,
|
|
777
|
-
Routes,
|
|
778
|
-
Route,
|
|
779
|
-
Navigate,
|
|
780
|
-
useLocation,
|
|
781
|
-
} from "react-router-dom"; // Componentes do roteador para gerenciar navegação
|
|
782
|
-
|
|
783
|
-
// Desestruturando configuração do Intlayer
|
|
784
|
-
const { internationalization, middleware } = configuration;
|
|
785
|
-
const { locales, defaultLocale } = internationalization;
|
|
786
|
-
|
|
787
|
-
/**
|
|
788
|
-
* Um componente que gerencia a localização e envolve os filhos com o contexto de local apropriado.
|
|
789
|
-
* Ele gerencia a detecção e validação da localidade baseada na URL.
|
|
790
|
-
*/
|
|
791
|
-
const AppLocalized = ({ children, locale }) => {
|
|
792
|
-
const { pathname, search } = useLocation(); // Obtém o caminho atual da URL
|
|
793
|
-
|
|
794
|
-
// Determina a localidade atual, usando a localidade padrão caso não seja fornecida
|
|
795
|
-
const currentLocale = locale ?? defaultLocale;
|
|
796
|
-
|
|
797
|
-
// Remove o prefixo da localidade do caminho para construir um caminho base
|
|
798
|
-
const pathWithoutLocale = getPathWithoutLocale(
|
|
799
|
-
pathname // Caminho atual da URL
|
|
800
|
-
);
|
|
801
|
-
|
|
802
|
-
/**
|
|
803
|
-
* Se middleware.prefixDefault for verdadeiro, a localidade padrão deve sempre ser prefixada.
|
|
804
|
-
*/
|
|
805
|
-
if (middleware.prefixDefault) {
|
|
806
|
-
// Valida a localidade
|
|
807
|
-
if (!locale || !locales.includes(locale)) {
|
|
808
|
-
// Redireciona para a localidade padrão com o caminho atualizado
|
|
809
|
-
return (
|
|
810
|
-
<Navigate
|
|
811
|
-
to={`/${defaultLocale}/${pathWithoutLocale}${search}`}
|
|
812
|
-
replace // Substitui a entrada atual do histórico pela nova
|
|
813
|
-
/>
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
// Envolve os filhos com o IntlayerProvider e define a localidade atual
|
|
818
|
-
return (
|
|
819
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
820
|
-
);
|
|
821
|
-
} else {
|
|
822
|
-
/**
|
|
823
|
-
* Quando middleware.prefixDefault é falso, a localidade padrão não é prefixada.
|
|
824
|
-
* Garante que a localidade atual seja válida e não a localidade padrão.
|
|
825
|
-
*/
|
|
826
|
-
if (
|
|
827
|
-
currentLocale.toString() !== defaultLocale.toString() &&
|
|
828
|
-
!locales
|
|
829
|
-
.filter(
|
|
830
|
-
(locale) => locale.toString() !== defaultLocale.toString() // Exclui a localidade padrão
|
|
831
|
-
)
|
|
832
|
-
.includes(currentLocale) // Verifica se a localidade atual está na lista de localidades válidas
|
|
833
|
-
) {
|
|
834
|
-
// Redireciona para o caminho sem o prefixo da localidade
|
|
835
|
-
return <Navigate to={`${pathWithoutLocale}${search}`} replace />;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// Envolve os filhos com o IntlayerProvider e define a localidade atual
|
|
839
|
-
return (
|
|
840
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
};
|
|
701
|
+
import { localeMap } from 'intlayer'; // Funções utilitárias e tipos do 'intlayer'
|
|
702
|
+
import type { FC, PropsWithChildren } from 'react'; // Tipos React para componentes funcionais e props
|
|
703
|
+
import { IntlayerProvider } from 'react-intlayer'; // Provedor para contexto de internacionalização
|
|
704
|
+
import { BrowserRouter, Route, Routes } from 'react-router-dom'; // Componentes do roteador para gerenciar navegação
|
|
844
705
|
|
|
845
706
|
/**
|
|
846
|
-
* Um componente de roteador que configura rotas específicas por
|
|
707
|
+
* Um componente de roteador que configura rotas específicas por idioma.
|
|
847
708
|
* Ele usa o React Router para gerenciar a navegação e renderizar componentes localizados.
|
|
848
709
|
*/
|
|
849
|
-
export const LocaleRouter = ({ children }) => (
|
|
710
|
+
export const LocaleRouter: FC<PropsWithChildren> = ({ children }) => (
|
|
850
711
|
<BrowserRouter>
|
|
851
712
|
<Routes>
|
|
852
|
-
{
|
|
853
|
-
|
|
854
|
-
(
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
/>
|
|
863
|
-
))}
|
|
864
|
-
|
|
865
|
-
{
|
|
866
|
-
// Se o prefixo da localidade padrão estiver desativado, renderiza os filhos diretamente na raiz
|
|
867
|
-
!middleware.prefixDefault && (
|
|
868
|
-
<Route
|
|
869
|
-
path="*"
|
|
870
|
-
element={
|
|
871
|
-
<AppLocalized locale={defaultLocale}>{children}</AppLocalized>
|
|
872
|
-
} // Envolve os filhos com o gerenciamento de localidade
|
|
873
|
-
/>
|
|
874
|
-
)
|
|
875
|
-
}
|
|
713
|
+
{localeMap(({ locale, urlPrefix }) => (
|
|
714
|
+
<Route
|
|
715
|
+
// Padrão de rota para capturar o idioma (ex: /en/, /fr/) e corresponder a todos os caminhos subsequentes
|
|
716
|
+
path={`${urlPrefix}/*`}
|
|
717
|
+
key={locale}
|
|
718
|
+
element={
|
|
719
|
+
<IntlayerProvider locale={locale}>{children}</IntlayerProvider>
|
|
720
|
+
} // Envolve os filhos com o gerenciamento de idioma
|
|
721
|
+
/>
|
|
722
|
+
))}
|
|
876
723
|
</Routes>
|
|
877
724
|
</BrowserRouter>
|
|
878
725
|
);
|
|
879
726
|
```
|
|
880
727
|
|
|
881
728
|
```jsx fileName="src/components/LocaleRouter.cjx" codeFormat="commonjs"
|
|
882
|
-
//
|
|
883
|
-
const
|
|
884
|
-
const { IntlayerProvider
|
|
885
|
-
const {
|
|
886
|
-
BrowserRouter,
|
|
887
|
-
Routes,
|
|
888
|
-
Route,
|
|
889
|
-
Navigate,
|
|
890
|
-
useLocation,
|
|
891
|
-
} = require("react-router-dom"); // Componentes do roteador para gerenciar navegação
|
|
892
|
-
|
|
893
|
-
// Desestruturando configuração do Intlayer
|
|
894
|
-
const { internationalization, middleware } = configuration;
|
|
895
|
-
const { locales, defaultLocale } = internationalization;
|
|
896
|
-
|
|
897
|
-
/**
|
|
898
|
-
* Um componente que gerencia a localização e envolve os filhos com o contexto de localidade apropriado.
|
|
899
|
-
* Ele gerencia a detecção e validação da localidade baseada na URL.
|
|
900
|
-
*/
|
|
901
|
-
const AppLocalized = ({ children, locale }) => {
|
|
902
|
-
const { pathname, search } = useLocation(); // Obtém o caminho atual da URL
|
|
903
|
-
|
|
904
|
-
// Determina o locale atual, usando o padrão caso não seja fornecido
|
|
905
|
-
const currentLocale = locale ?? defaultLocale;
|
|
906
|
-
|
|
907
|
-
// Remove o prefixo do locale do caminho para construir um caminho base
|
|
908
|
-
const pathWithoutLocale = getPathWithoutLocale(
|
|
909
|
-
pathname // Caminho atual da URL
|
|
910
|
-
);
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* Se middleware.prefixDefault for true, o locale padrão deve sempre ser prefixado.
|
|
914
|
-
*/
|
|
915
|
-
if (middleware.prefixDefault) {
|
|
916
|
-
// Valida o locale
|
|
917
|
-
if (!locale || !locales.includes(locale)) {
|
|
918
|
-
// Redireciona para o locale padrão com o caminho atualizado
|
|
919
|
-
return (
|
|
920
|
-
<Navigate
|
|
921
|
-
to={`/${defaultLocale}/${pathWithoutLocale}${search}`}
|
|
922
|
-
replace // Substitui a entrada atual do histórico pela nova
|
|
923
|
-
/>
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
// Envolve os filhos com o IntlayerProvider e define o locale atual
|
|
928
|
-
return (
|
|
929
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
930
|
-
);
|
|
931
|
-
} else {
|
|
932
|
-
/**
|
|
933
|
-
* Quando middleware.prefixDefault é falso, o locale padrão não é prefixado.
|
|
934
|
-
* Garante que o locale atual seja válido e não o locale padrão.
|
|
935
|
-
*/
|
|
936
|
-
if (
|
|
937
|
-
currentLocale.toString() !== defaultLocale.toString() &&
|
|
938
|
-
!locales
|
|
939
|
-
.filter(
|
|
940
|
-
(locale) => locale.toString() !== defaultLocale.toString() // Exclui o locale padrão
|
|
941
|
-
)
|
|
942
|
-
.includes(currentLocale) // Verifica se o locale atual está na lista de locales válidos
|
|
943
|
-
) {
|
|
944
|
-
// Redireciona para o caminho sem o prefixo do locale
|
|
945
|
-
return <Navigate to={`${pathWithoutLocale}${search}`} replace />;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// Envolve os filhos com o IntlayerProvider e define o locale atual
|
|
949
|
-
return (
|
|
950
|
-
<IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>
|
|
951
|
-
);
|
|
952
|
-
}
|
|
953
|
-
};
|
|
729
|
+
const { localeMap } = require("intlayer"); // Funções utilitárias e tipos do 'intlayer'
|
|
730
|
+
const React = require("react"); // Importar React
|
|
731
|
+
const { IntlayerProvider } = require("react-intlayer"); // Provedor para contexto de internacionalização
|
|
732
|
+
const { BrowserRouter, Route, Routes } = require("react-router-dom"); // Componentes do roteador para gerenciar navegação
|
|
954
733
|
|
|
955
734
|
/**
|
|
956
|
-
* Um componente de roteador que configura rotas específicas por
|
|
735
|
+
* Um componente de roteador que configura rotas específicas por idioma.
|
|
957
736
|
* Ele usa o React Router para gerenciar a navegação e renderizar componentes localizados.
|
|
958
737
|
*/
|
|
959
|
-
const LocaleRouter = ({ children }) =>
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
738
|
+
const LocaleRouter = ({ children }) =>
|
|
739
|
+
React.createElement(
|
|
740
|
+
BrowserRouter,
|
|
741
|
+
{},
|
|
742
|
+
React.createElement(
|
|
743
|
+
Routes,
|
|
744
|
+
{},
|
|
745
|
+
localeMap(({ locale, urlPrefix }) =>
|
|
746
|
+
React.createElement(Route, {
|
|
747
|
+
path: `${urlPrefix}/*`,
|
|
748
|
+
key: locale,
|
|
749
|
+
element: React.createElement(IntlayerProvider, { locale }, children),
|
|
750
|
+
})
|
|
751
|
+
)
|
|
752
|
+
)
|
|
753
|
+
);
|
|
974
754
|
|
|
975
|
-
|
|
976
|
-
// Se o prefixo para o locale padrão estiver desabilitado, renderiza os filhos diretamente na raiz
|
|
977
|
-
!middleware.prefixDefault && (
|
|
978
|
-
<Route
|
|
979
|
-
path="*"
|
|
980
|
-
element={
|
|
981
|
-
<AppLocalized locale={defaultLocale}>{children}</AppLocalized>
|
|
982
|
-
} // Envolve os filhos com o gerenciamento de locale
|
|
983
|
-
/>
|
|
984
|
-
)
|
|
985
|
-
}
|
|
986
|
-
</Routes>
|
|
987
|
-
</BrowserRouter>
|
|
988
|
-
);
|
|
755
|
+
exports.LocaleRouter = LocaleRouter;
|
|
989
756
|
```
|
|
990
757
|
|
|
758
|
+
> Nota: Se você usar `routing.mode: 'no-prefix' | 'search-params'`, provavelmente não precisará usar a função `localeMap`.
|
|
759
|
+
|
|
991
760
|
Então, você pode usar o componente `LocaleRouter` na sua aplicação:
|
|
992
761
|
|
|
993
762
|
```tsx fileName="src/App.tsx" codeFormat="typescript"
|
|
@@ -1027,7 +796,9 @@ const App = () => (
|
|
|
1027
796
|
);
|
|
1028
797
|
```
|
|
1029
798
|
|
|
1030
|
-
Paralelamente, você também pode usar o `intlayerProxy` para adicionar roteamento no lado do servidor à sua aplicação. Este plugin detectará automaticamente
|
|
799
|
+
Paralelamente, você também pode usar o `intlayerProxy` para adicionar roteamento no lado do servidor à sua aplicação. Este plugin detectará automaticamente a localidade atual com base na URL e definirá o cookie de localidade apropriado. Se nenhuma localidade for especificada, o plugin determinará a localidade mais adequada com base nas preferências de idioma do navegador do usuário. Se nenhuma localidade for detectada, ele redirecionará para a localidade padrão.
|
|
800
|
+
|
|
801
|
+
> Observe que para usar o `intlayerProxy` em produção, você precisa mover o pacote `vite-intlayer` de `devDependencies` para `dependencies`.
|
|
1031
802
|
|
|
1032
803
|
```typescript {3,7} fileName="vite.config.ts" codeFormat="typescript"
|
|
1033
804
|
import { defineConfig } from "vite";
|
|
@@ -1261,10 +1032,10 @@ const LocaleSwitcher = () => {
|
|
|
1261
1032
|
|
|
1262
1033
|
> Referências da documentação:
|
|
1263
1034
|
>
|
|
1264
|
-
> - [`useLocale` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
1265
|
-
> - [`getLocaleName` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
1266
|
-
> - [`getLocalizedUrl` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
1267
|
-
> - [`getHTMLTextDir` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/
|
|
1035
|
+
> - [`useLocale` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/react-intlayer/useLocale.md)
|
|
1036
|
+
> - [`getLocaleName` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/intlayer/getLocaleName.md)
|
|
1037
|
+
> - [`getLocalizedUrl` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/intlayer/getLocalizedUrl.md)
|
|
1038
|
+
> - [`getHTMLTextDir` hook](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/packages/intlayer/getHTMLTextDir.md)
|
|
1268
1039
|
> - [`hrefLang` attribute](https://developers.google.com/search/docs/specialty/international/localized-versions?hl=fr)
|
|
1269
1040
|
> - [`lang` attribute](https://developer.mozilla.org/pt-BR/docs/Web/HTML/Global_attributes/lang)
|
|
1270
1041
|
> - [`dir` attribute`](https://developer.mozilla.org/pt-BR/docs/Web/HTML/Global_attributes/dir)
|
|
@@ -1276,7 +1047,7 @@ Abaixo está o **Passo 9** atualizado com explicações adicionais e exemplos de
|
|
|
1276
1047
|
|
|
1277
1048
|
### (Opcional) Passo 9: Alterar os atributos de idioma e direção do HTML
|
|
1278
1049
|
|
|
1279
|
-
Quando sua aplicação suporta múltiplos idiomas, é crucial atualizar os atributos `lang` e `dir` da tag `<html>` para corresponder ao
|
|
1050
|
+
Quando sua aplicação suporta múltiplos idiomas, é crucial atualizar os atributos `lang` e `dir` da tag `<html>` para corresponder ao locale atual. Fazer isso garante:
|
|
1280
1051
|
|
|
1281
1052
|
- **Acessibilidade**: Leitores de tela e tecnologias assistivas dependem do atributo `lang` correto para pronunciar e interpretar o conteúdo com precisão.
|
|
1282
1053
|
- **Renderização de Texto**: O atributo `dir` (direção) assegura que o texto seja exibido na ordem correta (por exemplo, da esquerda para a direita para inglês, da direita para a esquerda para árabe ou hebraico), o que é essencial para a legibilidade.
|
|
@@ -1461,11 +1232,10 @@ import {
|
|
|
1461
1232
|
} from "react";
|
|
1462
1233
|
import { useLocale } from "react-intlayer";
|
|
1463
1234
|
|
|
1464
|
-
export interface LinkProps
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
> {}
|
|
1235
|
+
export interface LinkProps extends DetailedHTMLProps<
|
|
1236
|
+
AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
1237
|
+
HTMLAnchorElement
|
|
1238
|
+
> {}
|
|
1469
1239
|
|
|
1470
1240
|
/**
|
|
1471
1241
|
* Função utilitária para verificar se uma URL é externa.
|
|
@@ -1614,8 +1384,8 @@ Certifique-se de que sua configuração do TypeScript inclua os tipos gerados au
|
|
|
1614
1384
|
|
|
1615
1385
|
Para isso, você pode adicionar as seguintes instruções ao seu arquivo `.gitignore`:
|
|
1616
1386
|
|
|
1617
|
-
```plaintext
|
|
1618
|
-
#
|
|
1387
|
+
```plaintext fileName=".gitignore"
|
|
1388
|
+
# Ignore os arquivos gerados pelo Intlayer
|
|
1619
1389
|
.intlayer
|
|
1620
1390
|
```
|
|
1621
1391
|
|
|
@@ -1639,5 +1409,3 @@ Para mais detalhes sobre como usar a extensão, consulte a [documentação da Ex
|
|
|
1639
1409
|
### Avançar Mais
|
|
1640
1410
|
|
|
1641
1411
|
Para avançar mais, você pode implementar o [editor visual](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/intlayer_visual_editor.md) ou externalizar seu conteúdo usando o [CMS](https://github.com/aymericzip/intlayer/blob/main/docs/docs/pt/intlayer_CMS.md).
|
|
1642
|
-
|
|
1643
|
-
---
|
|
@@ -22,6 +22,7 @@ slugs:
|
|
|
22
22
|
- doc
|
|
23
23
|
- plugin
|
|
24
24
|
- sync-json
|
|
25
|
+
youtubeVideo: https://www.youtube.com/watch?v=MpGMxniDHNg
|
|
25
26
|
history:
|
|
26
27
|
- version: 6.1.6
|
|
27
28
|
date: 2025-10-05
|
|
@@ -30,6 +31,8 @@ history:
|
|
|
30
31
|
|
|
31
32
|
## Sync JSON (pontes i18n)
|
|
32
33
|
|
|
34
|
+
<iframe title="Como manter suas traduções JSON sincronizadas com Intlayer" class="m-auto aspect-[16/9] w-full overflow-hidden rounded-lg border-0" allow="autoplay; gyroscope;" loading="lazy" width="1080" height="auto" src="https://www.youtube.com/embed/MpGMxniDHNg?autoplay=0&origin=http://intlayer.org&controls=0&rel=1"/>
|
|
35
|
+
|
|
33
36
|
Use o Intlayer como um complemento para sua pilha i18n existente. Este plugin mantém suas mensagens JSON sincronizadas com os dicionários Intlayer para que você possa:
|
|
34
37
|
|
|
35
38
|
- Manter i18next, next-intl, react-intl, vue-i18n, next-translate, nuxt-i18n, Solid-i18next, svelte-i18n, etc.
|
|
@@ -430,7 +430,7 @@ export const App = () => {
|
|
|
430
430
|
```tsx fileName="src/components/LocaleSwitcher.tsx"
|
|
431
431
|
import { type FC } from "react";
|
|
432
432
|
import { getLocaleName } from "intlayer";
|
|
433
|
-
import { useLocale } from "intlayer";
|
|
433
|
+
import { useLocale } from "react-intlayer";
|
|
434
434
|
|
|
435
435
|
export const LocaleSwitcher: FC = () => {
|
|
436
436
|
const { setLocale, availableLocales, locale } = useLocale();
|