Поднимите интернационализацию Svelte на новый уровень

Работать с Svelte — одно удовольствие. Элегантный дизайн и надежные сторонние дополнения, которые можно подключить, превращают создание браузерных приложений в удовольствие.

Самым известным i18n-плагином для прогрессивного JavaScript-фреймворка Svelte, вероятно, является svelte-i18n.

Кристиан Кайзерманн, спасибо вам за этот замечательный i18n-плагин!

В этом руководстве мы добавим дополнительные суперспособности к svelte-i18n 😉.

TOC

  • Итак, как выглядит базовая настройка svelte-i18n? Давайте разберемся…
  • Можно ли сделать настройку svelte-18n еще лучше?
    • Необходимые условия
    • Начало работы
    • Переключатель языков
    • Где находятся дополнительные суперспособности?
    • Как это выглядит?
    • сохранять недостающие переводы
    • контекстное редактирование
    • 👀 но это еще не все…
    • 🎉🥳 Поздравляю 🎊🎁

Итак, как выглядит базовая установка svelte-i18n?

Давайте разберемся…

Необходимые условия

Убедитесь, что у вас установлены Node.js и npm. Лучше всего, если у вас есть некоторый опыт работы с простым HTML, JavaScript и базовым Svelte, прежде чем переходить к svelte-i18n.

Начало работы

Возьмите свой собственный проект Svelte или создайте новый.

Установим зависимость svelte-i18n:

npm install svelte-i18n

Создайте файл i18n.js:

import { addMessages, init, getLocaleFromNavigator } from 'svelte-i18n';

const fallbackLocale = 'en';
const lngs = [fallbackLocale, 'de'];

addMessages('en', {
  welcome: 'Welcome to Your Svelte App'
});
addMessages('de', {
  welcome: 'Willkommen zu deiner Svelte-App'
});

let initialLocale;
const detectedLocale = getLocaleFromNavigator(); // the locale could be region specific, i.e. de-CH
if (lngs.indexOf(detectedLocale) > -1) initialLocale = detectedLocale;
if (!initialLocale && detectedLocale.indexOf('-') > 0) {
  const foundLng = lngs.find((l) => detectedLocale.indexOf(l + '-') === 0);
  if (foundLng) initialLocale = foundLng;
}
if (!initialLocale) initialLocale = fallbackLocale;

init({
  fallbackLocale,
  initialLocale
});
Войдите в полноэкранный режим Выйти из полноэкранного режима

Импортируйте файл i18n.js в ваш файл main.js:

import App from './App.svelte';

import './i18n';

const app = new App({
  target: document.body,
  props: {}
});

export default app;
Войдите в полноэкранный режим Выйти из полноэкранного режима

Теперь давайте попробуем использовать наш первый интернационализированный текст.
В своем шаблоне импортируйте _ из svelte-i18n и используйте его следующим образом:

<script>
  import { _ } from 'svelte-i18n';
</script>

<main>
  <img alt="svelte logo" src="img/svelte-logo.png" />
  <h1>{$_('welcome')}</h1>
</main>
Войти в полноэкранный режим Выйти из полноэкранного режима

Отлично! Теперь давайте добавим еще один текстовый элемент…

<script>
  import { _ } from 'svelte-i18n';
</script>

<main>
  <img alt="svelte logo" src="img/svelte-logo.png" />
  <h1>{$_('welcome')}</h1>
  <p>{@html $_('descr', { values: { link: `<a href="https://svelte.dev/tutorial" target="_blank">${$_('doc')}</a>` } })}</p>
</main>
Войти в полноэкранный режим Выйти из полноэкранного режима

И соответствующие переводы:

addMessages('en', {
  welcome: 'Welcome to Your Svelte App',
  descr: 'Visit the {link} to learn how to build Svelte apps.',
  doc: 'Svelte tutorial'
});
addMessages('de', {
  welcome: 'Willkommen zu deiner Svelte-App',
  descr: 'Besuchen Sie den {link}, um zu erfahren, wie Sie Svelte-Apps erstellen.',
  doc: 'Svelte Tutorial'
});
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь, в зависимости от языка вашего браузера, вы должны увидеть что-то вроде этого:

Переключатель языков

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

<script>
  import { _, locale, locales } from 'svelte-i18n';
</script>

<main>
  <img alt="svelte logo" src="img/svelte-logo.png" />
  <h1>{$_('welcome')}</h1>
  <p>{@html $_('descr', { values: { link: `<a href="https://svelte.dev/tutorial" target="_blank">${$_('doc')}</a>` } })}</p>
  <select bind:value={$locale}>
    {#each $locales as locale}
      <option value={locale}>{locale}</option>
    {/each}
  </select>
</main>
Вход в полноэкранный режим Выход из полноэкранного режима

И мы сохраним текущий выбранный язык в localStorage:

import { addMessages, init, getLocaleFromNavigator, locale } from 'svelte-i18n';

const fallbackLocale = 'en';
const lngs = [fallbackLocale, 'de'];

addMessages('en', {
  welcome: 'Welcome to Your Svelte App',
  descr: 'Visit the {link} to learn how to build Svelte apps.',
  doc: 'Svelte tutorial'
});
addMessages('de', {
  welcome: 'Willkommen zu deiner Svelte-App',
  descr: 'Besuchen Sie den {link}, um zu erfahren, wie Sie Svelte-Apps erstellen.',
  doc: 'Svelte Tutorial'
});

locale.subscribe((lng) => {
  if (lng) localStorage.setItem('svelte-i18n-locale', lng);
});

let initialLocale;
const detectedLocale = localStorage.getItem('svelte-i18n-locale') || getLocaleFromNavigator(); // the locale could be region specific, i.e. de-CH
if (lngs.indexOf(detectedLocale) > -1) initialLocale = detectedLocale;
if (!initialLocale && detectedLocale.indexOf('-') > 0) {
  const foundLng = lngs.find((l) => detectedLocale.indexOf(l + '-') === 0);
  if (foundLng) initialLocale = foundLng;
}
if (!initialLocale) initialLocale = fallbackLocale;

init({
  fallbackLocale,
  initialLocale
});
Вход в полноэкранный режим Выход из полноэкранного режима

🥳 Потрясающе, вы только что создали свой первый переключатель языков!

А где же дополнительные суперспособности?

Давайте познакомимся с locizer…

locizer — это легкий модуль для доступа к данным из вашего проекта locize и использования их внутри вашего приложения.

Что такое locize?

Как это выглядит?

Сначала вам нужно зарегистрироваться в locize и войти в систему.
Затем создайте новый проект в locize и добавьте свои переводы. Вы можете добавить свои переводы либо путем импорта отдельных json-файлов, либо через API, либо с помощью CLI.

Наличие переводов в файле кода работает, но не очень удобно для работы переводчиков.
Использование locize отделяет переводы от кода.

После импорта все переводы должны выглядеть следующим образом:

Выполнив это, мы установим locize.

npm install locizer

Давайте адаптируем файл i18n.js:

import { register, init, getLocaleFromNavigator, locale } from 'svelte-i18n';
import locizer from 'locizer';

const fallbackLocale = 'en';
const lngs = [fallbackLocale, 'de'];
const namespace = 'messages'; // your namespace name added in locize

locizer.init({
  projectId: 'your-locize-project-id'
});

lngs.forEach((l) => {
  register(l, () => new Promise((resolve, reject) => {
    locizer.load(namespace, l, (err, ns) => {
      if (err) return reject(err);
      resolve(ns);
    });
  }));
})

locale.subscribe((lng) => {
  if (lng) localStorage.setItem('svelte-i18n-locale', lng);
});

let initialLocale;
const detectedLocale = localStorage.getItem('svelte-i18n-locale') || getLocaleFromNavigator();
if (lngs.indexOf(detectedLocale) > -1) initialLocale = detectedLocale;
if (!initialLocale && detectedLocale.indexOf('-') > 0) {
  const foundLng = lngs.find((l) => detectedLocale.indexOf(l + '-') === 0);
  if (foundLng) initialLocale = foundLng;
}
if (!initialLocale) initialLocale = fallbackLocale;

init({
  fallbackLocale,
  initialLocale
});
Вход в полноэкранный режим Выйти из полноэкранного режима

Поскольку переводы теперь загружаются асинхронно, мы также можем захотеть показать сообщение о загрузке, пока переводы не будут готовы:

<script>
  import { isLoading, _, locale, locales } from 'svelte-i18n';
</script>

<main>
  <img alt="svelte logo" src="img/svelte-logo.png" />
  {#if $isLoading}
    <p>
      loading translations...
    </p>
  {:else}
    <h1>{$_('welcome')}</h1>
    <p>{@html $_('descr', { values: { link: `<a href="https://svelte.dev/tutorial" target="_blank">${$_('doc')}</a>` } })}</p>
    <select bind:value={$locale}>
      {#each $locales as locale}
        <option value={locale}>{locale}</option>
      {/each}
    </select>
  {/if}
</main>
Войти в полноэкранный режим Выход из полноэкранного режима

Теперь ваши переводы загружаются непосредственно из CDN locize.

🙀 Это означает, что вы можете исправлять переводы без необходимости менять код или перераспределять приложение. 🤩

сохранять отсутствующие переводы

Хотелось бы, чтобы вновь добавленные в код ключи автоматически сохранялись в locize.

Ваше желание — моя команда!

Расширьте файл i18n.js api-ключом locize и функцией handleMissingMessage:

import { register, init, getLocaleFromNavigator, locale } from 'svelte-i18n';
import locizer from 'locizer';

const fallbackLocale = 'en';
const lngs = [fallbackLocale, 'de'];
const namespace = 'messages';
const apiKey = 'my-api-key'; // do not expose your API-Key in production

locizer.init({
  projectId: 'your-locize-project-id',
  apiKey
});

lngs.forEach((l) => {
  register(l, () => new Promise((resolve, reject) => {
    locizer.load(namespace, l, (err, ns) => {
      if (err) return reject(err);
      resolve(ns);
    });
  }));
})

locale.subscribe((lng) => {
  if (lng) localStorage.setItem('svelte-i18n-locale', lng);
});

let initialLocale;
const detectedLocale = localStorage.getItem('svelte-i18n-locale') || getLocaleFromNavigator();
if (lngs.indexOf(detectedLocale) > -1) initialLocale = detectedLocale;
if (!initialLocale && detectedLocale.indexOf('-') > 0) {
  const foundLng = lngs.find((l) => detectedLocale.indexOf(l + '-') === 0);
  if (foundLng) initialLocale = foundLng;
}
if (!initialLocale) initialLocale = fallbackLocale;

init({
  fallbackLocale,
  initialLocale,
  handleMissingMessage: apiKey ? ({ locale, id, defaultValue }) => {
    if (locale !== locizer.referenceLng) return;
    locizer.add(namespace, id, defaultValue);
  } : undefined
});
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, если вы добавите новый ключ в свои шаблоны, <h2>{$_('howAreYou', { default: 'How are you?' })}</h2>:

<script>
  import { isLoading, _, locale, locales } from 'svelte-i18n';
</script>

<main>
  <img alt="svelte logo" src="img/svelte-logo.png" />
  {#if $isLoading}
    <p>
      loading translations...
    </p>
  {:else}
    <h1>{$_('welcome')}</h1>
    <h2>{$_('howAreYou', { default: 'How are you?' })}</h2>
    <p>{@html $_('descr', { values: { link: `<a href="https://svelte.dev/tutorial" target="_blank">${$_('doc')}</a>` } })}</p>
    <select bind:value={$locale}>
      {#each $locales as locale}
        <option value={locale}>{locale}</option>
      {/each}
    </select>
  {/if}
</main>
Вход в полноэкранный режим Выход из полноэкранного режима

Он автоматически сохраняется в locize:

Наконец, с помощью рабочего процесса автоматического машинного перевода новые ключи не только автоматически добавляются в locize в процессе разработки приложения, но и автоматически переводятся на целевые языки с помощью машинного перевода:

Посмотрите это видео, чтобы увидеть, как выглядит рабочий процесс автоматического машинного перевода!

контекстное редактирование

Есть еще одна классная идея, которую мы можем сделать…

Давайте установим locize:

npm install locize

import { register, init, getLocaleFromNavigator, locale } from 'svelte-i18n';
import locizer from 'locizer';
import { addLocizeSavedHandler } from 'locize';

const fallbackLocale = 'en';
const lngs = [fallbackLocale, 'de'];
const namespace = 'messages';
const apiKey = 'my-api-key'; // do not expose your API-Key in production

locizer.init({
  projectId: 'your-locize-project-id',
  apiKey
});

lngs.forEach((l) => {
  register(l, () => new Promise((resolve, reject) => {
    locizer.load(namespace, l, (err, ns) => {
      if (err) return reject(err);
      resolve(ns);
    });
  }));
})

locale.subscribe((lng) => {
  if (lng) localStorage.setItem('svelte-i18n-locale', lng);
});

let initialLocale;
const detectedLocale = localStorage.getItem('svelte-i18n-locale') || getLocaleFromNavigator();
if (lngs.indexOf(detectedLocale) > -1) initialLocale = detectedLocale;
if (!initialLocale && detectedLocale.indexOf('-') > 0) {
  const foundLng = lngs.find((l) => detectedLocale.indexOf(l + '-') === 0);
  if (foundLng) initialLocale = foundLng;
}
if (!initialLocale) initialLocale = fallbackLocale;

init({
  fallbackLocale,
  initialLocale,
  handleMissingMessage: apiKey ? ({ locale, id, defaultValue }) => {
    if (locale !== locizer.referenceLng) return;
    locizer.add(namespace, id, defaultValue);
  } : undefined
});

addLocizeSavedHandler(() => location.reload());
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь откройте locize InContext Editor и удивитесь:

👀 но это еще не все…

Кэширование:

Слияние версий:

🧑💻 Код можно найти здесь.

🎉🥳 Поздравляю 🎊🎁.

Надеюсь, вы узнали несколько новых вещей о локализации Svelte и современных рабочих процессах локализации.

Так что если вы хотите поднять свою тему i18n на новый уровень, стоит попробовать locize.

👍

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *