Работать с 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.
👍