Получайте уведомления о предстоящих релизах Intlayer
    Создание:2025-04-18Последнее обновление:2025-06-29

    Начало работы с интернационализацией (i18n) с Intlayer, Vite и Preact

    Этот пакет находится в разработке. Подробнее смотрите в issue. Покажите свой интерес к Intlayer для Preact, поставив лайк этому issue.

    Смотрите шаблон приложения на GitHub.

    Что такое Intlayer?

    Intlayer - это инновационная, открытая библиотека интернационализации (i18n), созданная для упрощения поддержки многоязычности в современных веб-приложениях.

    С помощью Intlayer вы можете:

    • Легко управлять переводами с использованием декларативных словарей на уровне компонентов.
    • Динамически локализовать метаданные, маршруты и контент.
    • Обеспечить поддержку TypeScript с помощью автогенерируемых типов, улучшая автодополнение и обнаружение ошибок.
    • Воспользуйтесь расширенными возможностями, такими как динамическое определение и переключение локали.

    Пошаговое руководство по настройке Intlayer в приложении на Vite и Preact

    Шаг 1: Установка зависимостей

    Установите необходимые пакеты с помощью npm:

    bash
    npm install intlayer preact-intlayernpm install vite-intlayer --save-dev
    • intlayer

    • intlayer

      Основной пакет, предоставляющий инструменты интернационализации для управления конфигурацией, перевода, объявления контента, транспиляции и CLI-команд.

    • preact-intlayer Пакет, который интегрирует Intlayer с приложением на Preact. Он предоставляет провайдеры контекста и хуки для интернационализации Preact.

    • vite-intlayer Включает плагин Vite для интеграции Intlayer с сборщиком Vite, а также middleware для определения предпочтительной локали пользователя, управления cookie и обработки перенаправления URL.

    Шаг 2: Конфигурация вашего проекта

    Создайте файл конфигурации для настройки языков вашего приложения:

    intlayer.config.ts
    import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // Ваши другие локали    ],    defaultLocale: Locales.ENGLISH,  },};export default config;

    Через этот конфигурационный файл вы можете настроить локализованные URL, перенаправление в промежуточном ПО, имена cookie, расположение и расширение ваших деклараций контента, отключить логи Intlayer в консоли и многое другое. Для полного списка доступных параметров обратитесь к документации по конфигурации.

    Шаг 3: Интеграция Intlayer в вашу конфигурацию Vite

    Добавьте плагин intlayer в вашу конфигурацию.

    vite.config.ts
    import { defineConfig } from "vite";import preact from "@preact/preset-vite";import { intlayerPlugin } from "vite-intlayer";// https://vitejs.dev/config/export default defineConfig({  plugins: [preact(), intlayerPlugin()],});

    Плагин Vite intlayerPlugin() используется для интеграции Intlayer с Vite. Он обеспечивает сборку файлов деклараций контента и отслеживает их в режиме разработки. Определяет переменные окружения Intlayer внутри приложения Vite. Кроме того, предоставляет алиасы для оптимизации производительности.

    Шаг 4: Объявите Ваш Контент

    Создайте и управляйте декларациями контента для хранения переводов:

    src/app.content.tsx
    import { t, type Dictionary } from "intlayer";import type { ComponentChildren } from "preact";const appContent = {  key: "app",  content: {    viteLogo: t({      en: "Vite logo",      fr: "Logo Vite",      es: "Logo Vite",    }),    preactLogo: t({      en: "Preact logo",      fr: "Logo Preact",      es: "Логотип Preact",    }),    title: "Vite + Preact",    count: t({      en: "count is ",      fr: "le compte est ",      es: "el recuento es ",    }),    edit: t<ComponentChildren>({      en: (        <>          Редактируйте <code>src/app.tsx</code> и сохраните, чтобы проверить HMR        </>      ),      fr: (        <>          Éditez <code>src/app.tsx</code> et enregistrez pour tester HMR        </>      ),      es: (        <>          Edita <code>src/app.tsx</code> y guarda para probar HMR        </>      ),    }),    readTheDocs: t({      en: "Click on the Vite and Preact logos to learn more",      fr: "Cliquez sur les logos Vite et Preact pour en savoir plus",      es: "Haga clic en los logotipos de Vite y Preact para obtener más información",    }),  },} satisfies Dictionary;export default appContent;

    Ваши объявления контента могут быть определены в любом месте вашего приложения, как только они будут включены в каталог contentDir (по умолчанию, ./src). И соответствовать расширению файла объявления контента (по умолчанию, .content.{json,ts,tsx,js,jsx,mjs,mjx,cjs,cjx}).

    Для получения дополнительной информации обратитесь к документации по объявлениям контента.

    Если ваш файл контента содержит код TSX, возможно, вам потребуется импортировать import { h } from "preact"; или убедиться, что ваш JSX pragma правильно настроен для Preact.

    Шаг 5: Используйте Intlayer в вашем коде

    Получайте доступ к вашим словарям контента по всему приложению:

    src/app.tsx
    import { useState } from "preact/hooks";import type { FunctionalComponent } from "preact";import preactLogo from "./assets/preact.svg"; // Предполагается, что у вас есть preact.svgimport viteLogo from "/vite.svg";import "./app.css"; // Предполагается, что ваш CSS-файл называется app.cssimport { IntlayerProvider, useIntlayer } from "preact-intlayer";const AppContent: FunctionalComponent = () => {  const [count, setCount] = useState(0);  const content = useIntlayer("app");  return (    <>      <div>        <a href="https://vitejs.dev" target="_blank">          <img src={viteLogo} class="logo" alt={content.viteLogo.value} />        </a>        <a href="https://preactjs.com" target="_blank">          <img            src={preactLogo}            class="logo preact"            alt={content.preactLogo.value}          />        </a>      </div>      <h1>{content.title}</h1>      <div class="card">        <button onClick={() => setCount((count) => count + 1)}>          {content.count}          {count}        </button>        <p>{content.edit}</p>      </div>      <p class="read-the-docs">{content.readTheDocs}</p>    </>  );};const App: FunctionalComponent = () => (  <IntlayerProvider>    <AppContent />  </IntlayerProvider>);export default App;

    Если вы хотите использовать ваш контент в атрибуте типа string, таком как alt, title, href, aria-label и т.д., вы должны вызвать значение функции, например:

    jsx
    <img src={content.image.src.value} alt={content.image.value} />

    Примечание: В Preact className обычно пишется как class.

    Чтобы узнать больше о хуке useIntlayer, обратитесь к документации (API аналогично для preact-intlayer).

    (Необязательно) Шаг 6: Изменение языка вашего контента

    Для изменения языка вашего контента вы можете использовать функцию setLocale, предоставляемую хуком useLocale. Эта функция позволяет установить локаль приложения и обновить контент соответственно.

    src/components/LocaleSwitcher.tsx
    import type { FunctionalComponent } from "preact";import { Locales } from "intlayer";import { useLocale } from "preact-intlayer";const LocaleSwitcher: FunctionalComponent = () => {  const { setLocale } = useLocale();  return (    <button onClick={() => setLocale(Locales.ENGLISH)}>      Изменить язык на английский    </button>  );};export default LocaleSwitcher;

    Чтобы узнать больше о хуке useLocale, обратитесь к документации (API аналогично для preact-intlayer).

    (Необязательно) Шаг 7: Добавьте локализованную маршрутизацию в ваше приложение

    Цель этого шага - создать уникальные маршруты для каждого языка. Это полезно для SEO и URL, дружественных к SEO. Пример:

    plaintext
    - https://example.com/about- https://example.com/es/about- https://example.com/fr/about

    По умолчанию маршруты не имеют префикса для локали по умолчанию. Если вы хотите добавить префикс для локали по умолчанию, вы можете установить опцию middleware.prefixDefault в значение true в вашей конфигурации. Подробнее смотрите в документации по конфигурации.

    Чтобы добавить локализованную маршрутизацию в ваше приложение, вы можете создать компонент LocaleRouter, который обернет маршруты вашего приложения и будет обрабатывать маршрутизацию на основе локали. Вот пример с использованием preact-iso:

    Сначала установите preact-iso:

    bash
    npm install preact-iso
    src/components/LocaleRouter.tsx
    import { type Locales, configuration, getPathWithoutLocale } from "intlayer";import { ComponentChildren, FunctionalComponent } from "preact";import { IntlayerProvider } from "preact-intlayer";import { LocationProvider, useLocation } from "preact-iso";import { useEffect } from "preact/hooks";const { internationalization, middleware } = configuration;const { locales, defaultLocale } = internationalization;const Navigate: FunctionalComponent<{ to: string; replace?: boolean }> = ({  to,  replace,}) => {  const { route } = useLocation();  useEffect(() => {    route(to, replace);  }, [to, replace, route]);  return null;};/**/** * Компонент, который обрабатывает локализацию и оборачивает дочерние элементы в соответствующий контекст локали. * Он управляет определением и проверкой локали на основе URL. */const AppLocalized: FunctionalComponent<{  children: ComponentChildren;  locale?: Locales;}> = ({ children, locale }) => {  const { path: pathname, url } = useLocation();  if (!url) {    return null;  }  const search = url.substring(pathname.length);  // Определяем текущую локаль, используя локаль по умолчанию, если не указана  const currentLocale = locale ?? defaultLocale;  // Удаляем префикс локали из пути для построения базового пути  const pathWithoutLocale = getPathWithoutLocale(    pathname // Текущий путь URL  );  /**   * Если middleware.prefixDefault установлено в true, префикс с локалью по умолчанию должен всегда использоваться.   */  if (middleware.prefixDefault) {    // Проверка локали    if (!locale || !locales.includes(locale)) {      // Перенаправление на локаль по умолчанию с обновлённым путём      return (        <Navigate          to={`/${defaultLocale}/${pathWithoutLocale}${search}`}          replace // Заменить текущую запись в истории на новую        />      );    }    // Обернуть дочерние элементы в IntlayerProvider и установить текущую локаль    return (      <IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>    );  } else {    /**     * Когда middleware.prefixDefault равно false, префикс локали по умолчанию не используется.     * Убедитесь, что текущая локаль действительна и не является локалью по умолчанию.     */    if (      currentLocale.toString() !== defaultLocale.toString() &&      !locales        .filter(          (loc) => loc.toString() !== defaultLocale.toString() // Исключить локаль по умолчанию        )        .includes(currentLocale) // Проверить, входит ли текущая локаль в список допустимых локалей    ) {      // Перенаправить на путь без префикса локали      return <Navigate to={`${pathWithoutLocale}${search}`} replace />;    }    // Обернуть дочерние элементы в IntlayerProvider и установить текущую локаль    return (      <IntlayerProvider locale={currentLocale}>{children}</IntlayerProvider>    );  }};const RouterContent: FunctionalComponent<{  children: ComponentChildren;}> = ({ children }) => {  const { path } = useLocation();  if (!path) {    return null;  }  const pathLocale = path.split("/")[1] as Locales;  const isLocaleRoute = locales    .filter((locale) => middleware.prefixDefault || locale !== defaultLocale)    .some((locale) => locale.toString() === pathLocale);  if (isLocaleRoute) {    return <AppLocalized locale={pathLocale}>{children}</AppLocalized>;  }  return (    <AppLocalized      locale={!middleware.prefixDefault ? defaultLocale : undefined}    >      {children}    </AppLocalized>  );};/** * Компонент маршрутизатора, который настраивает маршруты с учетом локали. * Использует preact-iso для управления навигацией и рендеринга локализованных компонентов. */export const LocaleRouter: FunctionalComponent<{  children: ComponentChildren;}> = ({ children }) => (  <LocationProvider>    <RouterContent>{children}</RouterContent>  </LocationProvider>);

    Затем вы можете использовать компонент LocaleRouter в вашем приложении:

    src/app.tsx
    import { LocaleRouter } from "./components/LocaleRouter";import type { FunctionalComponent } from "preact";tsx fileName="src/app.tsx" codeFormat="typescript"import { LocaleRouter } from "./components/LocaleRouter";import type { FunctionalComponent } from "preact";// ... Ваш компонент AppContent (определённый на Шаге 5)const App: FunctionalComponent = () => (  <LocaleRouter>    <AppContent />  </LocaleRouter>);export default App;

    Параллельно вы также можете использовать intlayerMiddlewarePlugin для добавления маршрутизации на стороне сервера в ваше приложение. Этот плагин автоматически определит текущую локаль на основе URL и установит соответствующее cookie с локалью. Если локаль не указана, плагин определит наиболее подходящую локаль на основе языковых предпочтений браузера пользователя. Если локаль не будет обнаружена, произойдет перенаправление на локаль по умолчанию.

    vite.config.ts
    import { defineConfig } from "vite";import preact from "@preact/preset-vite";import { intlayerPlugin, intlayerMiddlewarePlugin } from "vite-intlayer";// https://vitejs.dev/config/export default defineConfig({  plugins: [preact(), intlayerPlugin(), intlayerMiddlewarePlugin()],});

    (Необязательно) Шаг 8: Изменение URL при смене локали

    Чтобы изменить URL при смене локали, вы можете использовать проп onLocaleChange, предоставляемый хуком useLocale. Параллельно вы можете использовать useLocation и route из preact-iso для обновления пути URL.

    src/components/LocaleSwitcher.tsx
    import { useLocation, route } from "preact-iso";import {  Locales,  getHTMLTextDir,  getLocaleName,  getLocalizedUrl,} from "intlayer";import { useLocale } from "preact-intlayer";import type { FunctionalComponent } from "preact";const LocaleSwitcher: FunctionalComponent = () => {  const location = useLocation();  const { locale, availableLocales, setLocale } = useLocale({    onLocaleChange: (newLocale) => {      const currentFullPath = location.url; // preact-iso предоставляет полный URL      // Формируем URL с обновлённой локалью      // Пример: /es/about?foo=bar      const pathWithLocale = getLocalizedUrl(currentFullPath, newLocale);      // Обновляем путь URL      route(pathWithLocale, true); // true для замены    },  });  return (    <div>      <button popovertarget="localePopover">{getLocaleName(locale)}</button>      <div id="localePopover" popover="auto">        {availableLocales.map((localeItem) => (          <a            href={getLocalizedUrl(location.url, localeItem)}            hreflang={localeItem}            aria-current={locale === localeItem ? "page" : undefined}            onClick={(e) => {              e.preventDefault();              setLocale(localeItem);              // Программная навигация после установки локали будет обработана в onLocaleChange            }}            key={localeItem}          >            <span>              {/* Локаль - например, FR */}              {localeItem}            </span>            <span>              {/* Язык на его собственной локали - например, Français */}              {getLocaleName(localeItem, localeItem)}            </span>            <span dir={getHTMLTextDir(localeItem)} lang={localeItem}>              {/* Язык на текущей локали - например, Francés при установленной локали Locales.SPANISH */}              {getLocaleName(localeItem, locale)}            </span>            <span dir="ltr" lang={Locales.ENGLISH}>              {/* Язык на английском - например, French */}              {getLocaleName(localeItem, Locales.ENGLISH)}            </span>          </a>        ))}      </div>    </div>  );};export default LocaleSwitcher;

    Ссылки на документацию:

    Ниже приведён обновлённый Шаг 9 с дополнительными пояснениями и уточнёнными примерами кода:


    (Необязательно) Шаг 9: Переключение атрибутов языка и направления в HTML

    Когда ваше приложение поддерживает несколько языков, крайне важно обновлять атрибуты lang и dir тега <html>, чтобы они соответствовали текущей локали. Это обеспечивает:

    • Доступность: Экранные читалки и вспомогательные технологии полагаются на правильный атрибут lang для корректного произношения и интерпретации контента.
    • Отображение текста: Атрибут dir (направление) гарантирует, что текст отображается в правильном порядке (например, слева направо для английского, справа налево для арабского или иврита), что важно для удобочитаемости.
    • SEO: Поисковые системы используют атрибут lang для определения языка вашей страницы, что помогает показывать правильный локализованный контент в результатах поиска.

    Обновляя эти атрибуты динамически при смене локали, вы обеспечиваете последовательный и доступный опыт для пользователей на всех поддерживаемых языках.

    Реализация хука

    Создайте пользовательский хук для управления атрибутами HTML. Хук отслеживает изменения локали и обновляет атрибуты соответствующим образом:

    src/hooks/useI18nHTMLAttributes.tsx
    import { useEffect } from "preact/hooks";import { useLocale } from "preact-intlayer";import { getHTMLTextDir } from "intlayer";/** * Обновляет атрибуты `lang` и `dir` элемента <html> в зависимости от текущей локали. * - `lang`: Информирует браузеры и поисковые системы о языке страницы. * - `dir`: Обеспечивает правильный порядок чтения (например, 'ltr' для английского, 'rtl' для арабского). * * Динамическое обновление необходимо для правильного отображения текста, доступности и SEO. */export const useI18nHTMLAttributes = () => {  const { locale } = useLocale();  useEffect(() => {    // Обновляет атрибут языка на текущую локаль.    document.documentElement.lang = locale;    // Устанавливает направление текста в зависимости от текущей локали.    document.documentElement.dir = getHTMLTextDir(locale);  }, [locale]);};

    Использование хука в вашем приложении

    Интегрируйте хук в ваш главный компонент, чтобы атрибуты HTML обновлялись при каждом изменении локали:

    src/app.tsx
    import type { FunctionalComponent } from "preact";import { IntlayerProvider } from "preact-intlayer"; // useIntlayer уже импортирован, если AppContent его требуетimport { useI18nHTMLAttributes } from "./hooks/useI18nHTMLAttributes";import "./app.css";// Определение AppContent из Шага 5const AppWithHooks: FunctionalComponent = () => {  // Применяем хук для обновления атрибутов lang и dir тега <html> в зависимости от локали.  useI18nHTMLAttributes();  // Предполагается, что AppContent - это ваш основной компонент отображения контента из Шага 5  return <AppContent />;};const App: FunctionalComponent = () => (  <IntlayerProvider>    <AppWithHooks />  </IntlayerProvider>);export default App;

    Применяя эти изменения, ваше приложение будет:

    • Обеспечивать корректное отражение текущей локали в атрибуте языка (lang), что важно для SEO и поведения браузера.
    • Настраивать направление текста (dir) в соответствии с локалью, улучшая читаемость и удобство использования для языков с разным порядком чтения.
    • Обеспечить более доступный опыт, так как вспомогательные технологии зависят от этих атрибутов для оптимальной работы.

    Чтобы гарантировать, что навигация вашего приложения учитывает текущую локаль, вы можете создать пользовательский компонент Link. Этот компонент автоматически добавляет префикс текущего языка к внутренним URL.

    Это поведение полезно по нескольким причинам:

    • SEO и пользовательский опыт: Локализованные URL помогают поисковым системам правильно индексировать страницы на разных языках и предоставлять пользователям контент на предпочитаемом языке.
    • Последовательность: Используя локализованную ссылку во всем приложении, вы гарантируете, что навигация останется в пределах текущей локали, предотвращая неожиданные переключения языка.
    • Поддерживаемость: Централизация логики локализации в одном компоненте упрощает управление URL.

    Для Preact с preact-iso обычно используются стандартные теги <a> для навигации, а маршрутизацию обрабатывает preact-iso. Если вам нужна программная навигация по клику (например, чтобы выполнить действия перед переходом), вы можете использовать функцию route из useLocation. Вот как можно создать пользовательский компонент ссылки, который локализует URL:

    src/components/LocalizedLink.tsx
    import { getLocalizedUrl } from "intlayer";import { useLocale, useLocation, route } from "preact-intlayer"; // Предполагается, что useLocation и route могут быть из preact-iso через preact-intlayer, если они реэкспортированы, или импортировать напрямую// Если не реэкспортируется, импортируйте напрямую: import { useLocation, route } from "preact-iso";import type { JSX } from "preact"; // Для HTMLAttributesimport { forwardRef } from "preact/compat"; // Для передачи refexport interface LocalizedLinkProps  extends JSX.HTMLAttributes<HTMLAnchorElement> {  href: string;  replace?: boolean; // Необязательно: для замены состояния истории}/** * Вспомогательная функция для проверки, является ли данный URL внешним. * Если URL начинается с http:// или https://, он считается внешним. */export const checkIsExternalLink = (href?: string): boolean =>  /^https?:\/\//.test(href ?? "");/** * Кастомный компонент Link, который адаптирует атрибут href в зависимости от текущей локали. * Для внутренних ссылок используется `getLocalizedUrl` для добавления префикса локали к URL (например, /fr/about). * Это гарантирует, что навигация останется в контексте той же локали. * Используется стандартный тег <a>, но может инициировать навигацию на стороне клиента с помощью `route` из preact-iso. */export const LocalizedLink = forwardRef<HTMLAnchorElement, LocalizedLinkProps>(  ({ href, children, onClick, replace = false, ...props }, ref) => {    const { locale } = useLocale();    const location = useLocation(); // из preact-iso    const isExternalLink = checkIsExternalLink(href);    const hrefI18n =      href && !isExternalLink ? getLocalizedUrl(href, locale) : href;    const handleClick = (event: JSX.TargetedMouseEvent<HTMLAnchorElement>) => {      if (onClick) {        onClick(event);      }      if (        !isExternalLink &&        href && // Убедиться, что href определён        event.button === 0 && // Левая кнопка мыши        !event.metaKey &&        !event.ctrlKey &&        !event.shiftKey &&        !event.altKey && // Проверка стандартных модификаторов        !props.target // Не открывать в новой вкладке/окне      ) {        event.preventDefault();        if (location.url !== hrefI18n) {          // Переходить только если URL отличается          route(hrefI18n, replace); // Использовать route из preact-iso        }      }    };    return (      <a href={hrefI18n} ref={ref} onClick={handleClick} {...props}>        {children}      </a>    );  });

    Как это работает

    • Определение внешних ссылок:
      Вспомогательная функция checkIsExternalLink определяет, является ли URL внешним. Внешние ссылки остаются без изменений.
    • Получение текущей локали:
      Хук useLocale предоставляет текущую локаль.
    • Локализация URL:
      Для внутренних ссылок функция getLocalizedUrl добавляет префикс текущей локали к URL.
    • Навигация на стороне клиента: Функция handleClick проверяет, является ли ссылка внутренней и нужно ли предотвратить стандартную навигацию. Если да, она использует функцию route из preact-iso (полученную через useLocation или импортированную напрямую) для выполнения навигации на стороне клиента. Это обеспечивает поведение, похожее на SPA, без полной перезагрузки страницы.
    • Возврат ссылки:
      Компонент возвращает элемент <a> с локализованным URL и пользовательским обработчиком клика.

    Настройка TypeScript

    Intlayer использует расширение модулей (module augmentation), чтобы получить преимущества TypeScript и сделать вашу кодовую базу более надежной.

    alt text

    alt text

    Убедитесь, что ваша конфигурация TypeScript включает автоматически сгенерированные типы.

    tsconfig.json
    {  // ... Ваши существующие конфигурации TypeScript  "compilerOptions": {    // ...    "jsx": "react-jsx",    "jsxImportSource": "preact", // Рекомендуется для Preact 10+    // ...  },  "include": [    // ... Ваши существующие конфигурации TypeScript    ".intlayer/**/*.ts", // Включить автоматически сгенерированные типы  ],}

    Убедитесь, что ваш tsconfig.json настроен для Preact, особенно параметры jsx и jsxImportSource или jsxFactory/jsxFragmentFactory для старых версий Preact, если вы не используете настройки по умолчанию preset-vite.

    Конфигурация Git

    Рекомендуется игнорировать файлы, сгенерированные Intlayer. Это позволит избежать их коммита в ваш Git-репозиторий.

    Чтобы сделать это, вы можете добавить следующие инструкции в ваш файл .gitignore:

    plaintext
    # Игнорировать файлы, сгенерированные Intlayer.intlayer

    Расширение VS Code

    Для улучшения вашего опыта разработки с Intlayer вы можете установить официальное расширение Intlayer для VS Code.

    Установить из магазина расширений VS Code

    Это расширение предоставляет:

    • Автозаполнение ключей переводов.
    • Обнаружение ошибок в реальном времени для отсутствующих переводов.
    • Встроенный просмотр переведенного контента.
    • Быстрые действия для удобного создания и обновления переводов.

    Для получения дополнительной информации о том, как использовать расширение, обратитесь к документации по расширению Intlayer для VS Code.


    Продвинутые возможности

    Для расширения возможностей вы можете реализовать визуальный редактор или вынести ваш контент с помощью CMS.


    История документации

    • 5.5.10 - 2025-06-29: Инициализация истории
    Получайте уведомления о предстоящих релизах Intlayer