首頁  >  文章  >  web前端  >  使用 i 翻譯你的 React 專案從未如此簡單

使用 i 翻譯你的 React 專案從未如此簡單

王林
王林原創
2024-08-24 11:04:05506瀏覽

Nunca foi tão fácil TRADUZIR seu projeto React com i

說瘋狂的開發者!

今天我將向您展示,將整個專案翻譯成 React 從未像現在這樣容易。但首先您需要知道為什麼這很重要。

當人們開始程式設計時,程式碼文字和訊息通常使用葡萄牙語 (pt-BR)。將項目翻譯成其他語言從來都不是優先事項,並且被認為是複雜或不必要的。

那為什麼它會相關呢?

這取決於您的實際情況。以下是您應該考慮此過程的一些原因:

公司需要
可能是您工作的公司或您擁有的某些 SaaS 開始在另一個國家/地區運作並且有此需求。具有此功能的產品有巨大的差異。

國際職缺申請
如果您正在申請國際職缺,擁有包含國際化專案的作品集可以為您帶來引人注目的亮點。這表明您已準備好從事全球項目,並且不像大多數人一樣懶惰。

你絕對不會學太多
國際化不僅是一種特徵,也是一種重要的學習經驗。這是您的技能和工具庫中的另一種武器。

過去是如何做到的?

專案翻譯已經是個老問題了。人們在 HTML 中選擇了該國家的國旗,供人們選擇,並在程式碼中用 if 填充以了解將顯示哪些文字。
這是非常被忽略的。網站是用單一語言製作的,翻譯是隨意添加的。如果是在後端的話,交易會更糟。

隨著網路的全球化,對多語言軟體的需求不斷增長,帶來了針對國際化的特定工具。像 GNU Gettext 這樣的解決方案出現在後端,隨後出現了像 i18next 和 react-intl 這樣的前端函式庫。這就是疑問所在......

i18next 比較React-intl:選擇哪一個?

  • i18next:這個出現於 2011 年,它是一個 npm 包,適用於客戶端的 Node.js 和 SPA。社群採納了它,並最終於2015年在react-i18next lib中製作了React版本。因此,作為正面和負面的觀點,我們有:

    • 優點:彈性、路上時間(自 2011 年起)、龐大的生態系統(一次學習,隨處翻譯)和自動回退。
    • 缺點:學習曲線。有很多文檔需要閱讀,但並不是您需要的所有內容都在那裡。
  • react-intl:FormatJS 專案的一部分,遵循國際 JavaScript API 標準,確保與現代瀏覽器的相容性。

    • 優點:與 ECMAScript 標準保持一致,整合簡單。
    • 缺點:靈活性較差,插件支援較少。

我們將使用哪一個?

i18next 我的朋友們!我總是建議閱讀文件來開始,但讓我們看看 Doido 的指南!

使用 i18next 國際化 React 應用程式

  1. 安裝
   npm install i18next i18next-chained-backend i18next-http-backend i18next-resources-to-backend react-i18next next-i18next 

  1. 設定:建立一個i18n.js來設定i18next。
   import i18n from 'i18next';
   import { initReactI18next } from 'react-i18next';
   import Backend from 'i18next-http-backend';
   import LanguageDetector from 'i18next-browser-languagedetector';

   i18n
     .use(Backend)
     .use(LanguageDetector)
     .use(initReactI18next)
     .init({ fallbackLng: 'en', interpolation: { escapeValue: false } });

   export default i18n;
  1. 翻譯:在 locales/en/translation.json 和 locales/pt/translation.json 中建立翻譯檔案。
   {
     "welcome": "Welcome to our application!",
     "login": "Login"
   }
  1. 翻譯的使用:在React中使用useTranslation鉤子。
   import React from 'react';
   import { useTranslation } from 'react-i18next';

   function App() {
     const { t } = useTranslation();

     return (
       <div>
         <h1>{t('welcome')}</h1>
         <button>{t('login')}</button>
       </div>
     );
   }

   export default App;
  1. 語言變更:允許使用者更改語言。
   import React from 'react';
   import { useTranslation } from 'react-i18next';

   function LanguageSwitcher() {
     const { i18n } = useTranslation();

     const changeLanguage = (lng) => i18n.changeLanguage(lng);

     return (
       <div>
         <button onClick={() => changeLanguage('en')}>English</button>
         <button onClick={() => changeLanguage('pt')}>Português</button>
       </div>
     );
   }

   export default LanguageSwitcher;

這就是全部嗎?

當然不是,我現在將向您展示我在 CrazyStack 專案中所做的事情。首先,我在 Nextjs 中做了一個不同的配置,採用了我在專案本身的公共資料夾中定義的靜態 JSON!看看:

import i18next from "i18next";
import ChainedBackend from "i18next-chained-backend";
import HttpBackend from "i18next-http-backend";
import resourcesToBackend from "i18next-resources-to-backend";
import { initReactI18next } from "react-i18next";
import { defaultTexts } from "./defaultTexts";

i18next
  .use(ChainedBackend)
  .use(initReactI18next)
  .init({
    lng: "pt-br",
    fallbackLng: "pt-br",
    interpolation: {
      escapeValue: false,
    },
    compatibilityJSON: "v3",
    react: {
      //wait: true,//usar no react native
      useSuspense: false,
    },
    backend: {
      backends: [HttpBackend, resourcesToBackend(defaultTexts)],
      backendOptions: [
        {
          loadPath: `${process.env.NEXT_PUBLIC_URL}/{{lng}}/{{ns}}.json`,
        },
      ],
    },
  });

然後我創建了一個上下文 API 來保存語言並在整個專案中存取它。從導入開始

2. 進口

import { useTranslation } from "react-i18next";
import { createContext, useState, useContext } from "react";
  • useTranslation:React-i18next 掛鉤來存取翻譯功能。在這裡,您將在專案中的幾乎每個 JSX 元件中使用它。
  • createContext、useState、useContext:用於建立和使用上下文以及管理狀態的 React 函數。

3. 上下文創建

const I18NContext = createContext({} as any);

建立上下文來透過 DOM 儲存和提供資料(例如當前語言)。

4. Verificação do Ambiente

export const isBrowser = typeof window !== "undefined";

Essa linha verifica se o código está sendo executado no navegador (em vez de no servidor), essencial para manipular recursos específicos do cliente, como localStorage.

5. Componente I18nProvider

export const I18nProvider = ({ children }: any) => {
  const { i18n } = useTranslation() || {};
  const [currentLanguage, setCurrentLanguage] = useState(
    formatLanguageFromi18N(i18n?.language)
  );
  const changeLanguage = (language) => {
    setCurrentLanguage(language);
    i18n?.changeLanguage?.(formatLanguageFromSelect(language));
    localStorage.setItem("language", formatLanguageFromSelect(language));
  };
  return (
    <I18NContext.Provider value={{ changeLanguage, currentLanguage, setCurrentLanguage }}>
      {children}
    </I18NContext.Provider>
  );
};

Este componente é um provider que envolve a árvore de componentes React e fornece o estado atual do idioma e a função para alterá-lo.

  • useTranslation: Recupera o objeto i18n da biblioteca react-i18next, que contém informações sobre o idioma atual.
  • currentLanguage: Estado que armazena o idioma atual, inicializado com base no idioma detectado pelo i18n.
  • changeLanguage: Função para alterar o idioma, que também salva a escolha no localStorage para persistência entre recargas da página.

6. Hook useI18n

export const useI18n = () => {
  if (!isBrowser) {
    return {
      currentLanguage: "pt-br",
      setCurrentLanguage: () => {},
      changeLanguage: () => {},
    };
  }
  return useContext(I18NContext);
};

Este hook facilita o acesso ao contexto de internacionalização em qualquer componente.

  • Verifica se está no navegador (isBrowser). Se não estiver, retorna valores padrão para evitar erros no server side.
  • Se estiver no navegador, consome e retorna o contexto I18NContext.

7. Mapas de Conversão

const countryToLanguage = {
  BR: "pt-br",
  US: "en",
};
const languageToCountry = {
  "pt-br": "BR",
  en: "US",
};

Esses objetos mapeiam códigos de países para códigos de idiomas e vice-versa, facilitando a formatação dos códigos de idioma entre diferentes convenções.

8. Funções de Formatação

export const formatLanguageFromi18N = (language) => languageToCountry[language];
export const formatLanguageFromSelect = (language) => countryToLanguage[language];

Essas funções formatam os códigos de idioma conforme necessário. formatLanguageFromi18N converte o código de idioma para o código do país, enquanto formatLanguageFromSelect faz a conversão inversa.

Código completo

"use client";
import { useTranslation } from "react-i18next";
import { createContext, useState, useContext } from "react";

const I18NContext = createContext({} as any);

export const isBrowser = typeof window !== "undefined";

export const I18nProvider = ({ children }: any) => {
  const { i18n } = useTranslation() || {};
  const [currentLanguage, setCurrentLanguage] = useState(
    formatLanguageFromi18N(i18n?.language)
  );
  const changeLanguage = (language) => {
    setCurrentLanguage(language);
    i18n?.changeLanguage?.(formatLanguageFromSelect(language));
    localStorage.setItem("language", formatLanguageFromSelect(language));
  };
  return (
    <I18NContext.Provider value={{ changeLanguage, currentLanguage, setCurrentLanguage }}>
      {children}
    </I18NContext.Provider>
  );
};

export const useI18n = () => {
  if (!isBrowser) {
    return {
      currentLanguage: "pt-br",
      setCurrentLanguage: () => {},
      changeLanguage: () => {},
    };
  }
  return useContext(I18NContext);
};

const countryToLanguage = {
  BR: "pt-br",
  US: "en",
};

const languageToCountry = {
  "pt-br": "BR",
  en: "US",
};

export const formatLanguageFromi18N = (language) => languageToCountry[language];
export const formatLanguageFromSelect = (language) => countryToLanguage[language];

Depois eu mexi na Navbar

No código eu tenho um select de idioma utilizando um dropdown de países. Olha só:

"use client";
//@ts-nocheck
import { Header, Flex, Logo, Profile, NotificationsNav, SearchBar } from "@/shared/ui";
import { useBreakpointValue, Icon, IconButton, useMediaQuery } from "@chakra-ui/react";
import { RiMenuLine } from "react-icons/ri";
import { useAuth, useSidebarDrawer } from "@/shared/libs";
import { useEffect, useState } from "react";
import { CountryDropdown } from "react-country-region-selector";
import { theme } from "@/application/theme";
import { formatLanguageFromi18N, useI18n } from "@/application/providers/i18nProvider";
import { useTranslation } from "react-i18next";

export const NavBar = ({ showLogo = true }) => {
  const { isAuthenticated } = useAuth() || {};
  const { i18n } = useTranslation();
  const { changeLanguage, setCurrentLanguage } = useI18n() || {};
  const { onOpen = () => {}, onClose } = useSidebarDrawer() || {};
  const isDesktopVersion = useBreakpointValue({ base: false, lg: true });
  const [country, setCountry] = useState(formatLanguageFromi18N(i18n?.language));
  useEffect(() => {
    return () => {
      onClose?.();
    };
  }, []);
  const Dropdown = CountryDropdown as any;
  useEffect(() => {
    const language = localStorage.getItem("language");
    if (language) {
      setCountry(formatLanguageFromi18N(language));
      setCurrentLanguage(language);
      i18n?.changeLanguage?.(language);
    }
  }, []);
  return (
    <Header>
      <Flex alignItems={"center"} w={"100%"}>
        {isAuthenticated && !isDesktopVersion && (
          <IconButton
            aria-label="Open sidebar"
            fontSize="24"
            icon={<Icon as={RiMenuLine} />}
            variant="unstyled"
            onClick={onOpen}
            mr="1"
            mt={2}
          />
        )}
        <Logo marginBottom={0} />
        {/* {isLargerThan560 && (
          <SearchBar placeholder="Pesquise por nome..." name="search" width="auto" />
        )} */}
        {isAuthenticated && (
          <Flex align="center" ml="auto">
            {/* <NotificationsNav /> */}
            <Dropdown
              value={country}
              onChange={(val) => {
                setCountry(val);
                changeLanguage(val);
              }}
              labelType="short"
              valueType="short"
              showDefaultOption
              defaultOptionLabel="Selecione o idioma"
              whitelist={["US", "BR"]}
              style={{
                backgroundColor: theme.colors.secondary[400],
                padding: 10,
                width: 60,
                marginRight: 15,
                borderRadius: 8,
              }}
            />
            <Profile showProfileData={isDesktopVersion} />
          </Flex>
        )}
      </Flex>
    </Header>
  );
};

Importações e Setup Inicial:

  • useAuth: Verifica se o usuário está autenticado.
  • useBreakpointValue: Determina se a versão desktop deve ser exibida com base no tamanho da tela.
  • useState: Define o estado inicial do país/língua (country) usando a função formatLanguageFromi18N para formatar a língua atual do i18n.
  • useEffect: Primeiro efeito limpa o sidebar ao desmontar o componente (onClose). O segundo efeito verifica se o idioma está salvo no localStorage e, caso esteja, atualiza o estado country e muda o idioma na aplicação.

Dropdown de Idiomas:

  • O dropdown é implementado usando o componente CountryDropdown da biblioteca react-country-region-selector, que é customizado para servir como um seletor de idioma.
  • value={country}: O valor selecionado no dropdown é controlado pelo estado country.
  • onChange={(val) => { ... }}: Quando o valor do dropdown é alterado, o estado country é atualizado, e a função changeLanguage é chamada para alterar o idioma da aplicação.
  • whitelist={["US", "BR"]}: Restringe as opções do dropdown a "US" (inglês) e "BR" (português).
  • style={...}: Estilização inline personalizada para o dropdown, utilizando cores e espaçamentos do tema theme.
  1. Comportamento do Seletor de Idioma:
    • O dropdown permite que o usuário selecione o idioma preferido, e essa seleção é persistida no localStorage.
    • Ao mudar o idioma, o dropdown reflete essa mudança, e a aplicação é atualizada para usar o novo idioma selecionado. Para incluir o trecho de código que você forneceu na imagem no seu artigo, você pode seguir este formato:

E como mudar os textos?

De componente em componente eu fui fazendo o mesmo procedimento. O código abaixo mostra como substituir o texto estático por uma tradução dinâmica baseada na chave de localização:

import { Divider } from "@chakra-ui/react";
import { IoExitOutline } from "react-icons/io5";
import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";  // Importando o hook useTranslation

type ProfileProps = {
  showProfileData?: boolean;
};

export const Profile = ({ showProfileData }: ProfileProps) => {
  const { t } = useTranslation(["PAGES"]);  // Obtendo a função t para tradução
  const { user, logout } = useAuth() || {};
  const router = useRouter();
  const { showUserMenu, setShowUserMenu } = useProfile();

  return (
    <Box>
      {/* Outras partes do componente */}
      <Flex>
        <IoExitOutline />
        <Text fontSize="sm">
          {t("PAGES:HOME_PAGE.logout", { defaultValue: "Sair" })}  // Chave de tradução com valor padrão
        </Text>
      </Flex>
    </Box>
  );
};

Neste exemplo, o hook useTranslation é utilizado para carregar a chave de tradução PAGES:HOME_PAGE.logout. Se a chave não for encontrada, o texto padrão "Sair" será exibido.

Conclusão

A ideia pode ser aplicada em qualquer componente de texto estático. Basta usar a hook useTranslation.
Internacionalizar sua aplicação pode abrir portas para mercados globais, destacar seu portfólio e aprimorar suas habilidades. Escolher entre i18next e react-intl depende das necessidades específicas do seu projeto, mas ambos são excelentes opções para quem deseja começar.

Sugestão de cursos

Em 2022 eu criei o bootcamp CrazyStack. Nele, eu mostro 2 aplicações completas de um sistema de agendamentos online de serviços aplicando conceitos avançados como Design Patterns, Clean Architecture, Feature Sliced Design, SOLID, DDD, além de Testes unitários, de integração e E2E.

在第一個應用程式中,您將學習如何在 Node.js 生態系統中建立 REST API。將建立涉及複雜業務規則的用例,例如列出可用時間、根據預訂產生訂單、忠誠度系統、佣金、付款、客戶評論等等。一切都在 TypeScript 中完成並使用非關聯式資料庫 MongoDB。

在第二個應用程式中,您將學習如何在 React.js 生態系統中建立管理面板來查看圖表和操作記錄。一切都是透過 TypeScript 和 Next.js 框架完成的。此外,還將使用Chakra UI視覺化元件庫,將原子設計概念應用於已建立的元件。要了解更多信息,請訪問crazystack.com.br。

以上是使用 i 翻譯你的 React 專案從未如此簡單的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn