# Як працювати над локалізованим клієнтським вебзастосунком

Клієнтський вебзастосунок на основі React, який забезпечує роботу нашої навчальної платформи, побудований за допомогою Gatsby. Він перекладений різними мовами світу, використовуючи [react-i18next](https://react.i18next.com/) та [i18next](https://www.i18next.com/).

Ви можете дізнатись більше про локальне налаштування клієнтського застосунку, дотримуючись [цього посібника](how-to-setup-freecodecamp-locally.md). Застосунок доступний лише англійською за замовчуванням.

Як тільки ви налаштували проєкт локально, дотримуйтесь цієї документації, щоб запустити клієнта потрібною мовою зі списку доступних мов.

Це може бути корисно, якщо ви працюєте над функціональністю, яка конкретно націлена на щось, що передбачає локалізацію та вимагає, наприклад, підтвердження кнопки з надписом іншою мовою.

> [!TIP] Вам не потрібно дотримуватись цього документу для перекладу навчальної програми чи документації freeCodeCamp. Натомість див. [цей посібник](how-to-translate-files.md).

З’ясуємо, як працюють фреймворки та інструменти i18n.

## Структура файлів

Більшість файлів для перекладу платформи розташовані у папці [`client/i18n`](https://github.com/freeCodeCamp/freeCodeCamp/tree/main/client/i18n). Для кожної мови є каталог, що містить файли JSON з перекладами.

```console
  config
  └── i18n.ts
  ...
  client/i18n
  ├── configForTests.js
  ├── config.js
  ├── locales
  │   ├── chinese
  │   │   ├── intro.json
  │   │   ├── links.json
  │   │   ├── meta-tags.json
  │   │   ├── motivation.json
  │   │   └── translations.json
  ... ...
  │   ├── dothraki
  │   │   ├── intro.json
  │   │   ├── links.json
  │   │   ├── meta-tags.json
  │   │   ├── motivation.json
  │   │   └── translations.json
  ... ...
  │   ├── english
  │   │   ├── intro.json
  │   │   ├── links.json
  │   │   ├── meta-tags.json
  │   │   ├── motivation.json
  │   │   └── translations.json
  │   └── espanol
  │       ├── intro.json
  │       ├── links.json
  │       ├── meta-tags.json
  │       ├── motivation.json
  │       └── translations.json
  ├── locales.test.js
  ├── schema-validation.js
  └── validate-keys.ts
```

Деякі з цих файлів перекладаються на нашій перекладацькій платформі (Crowdin), а інші перекладені чи створені через PR на GitHub.

**Файли, перекладені на нашій перекладацькій платформі:**

- Файл `translations.json` містить більшу частину тексту, який з’являється на елементах інтерфейсу користувача. Для отримання потрібного тексту встановленої мови у кодовій базі використовуються ключі. Цей файл повинен містити одинакові ключі в усіх мовах.

- Файл `intro.json` містить пари ключ-значення для вступного тексту на сторінках сертифікації.

  Якщо ви хочете додати/оновити переклад для ключів, див. [цей посібник](how-to-translate-files.md).

**Файли, НЕ перекладені на нашій перекладацькій платформі:**

- У файлах `motivation.json` не обов’язково повинні бути однакові цитати, вербальне оцінювання чи довжина масивів. Лише однакова структура JSON.

- Файл `meta-tags.json` містить інформацію про наш вебсайт для метатегів.

  Зазвичай ці файли змінює персонал. Якщо ви помітили щось незвичне, ми радимо зв’язатися з нами у [чаті](https://discord.gg/PRyKn3Vbay).

## Тестування клієнтського застосунку світовою мовою

Ви можете протестувати клієнтський застосунок будь-якою мовою [ зі списку `availableLangs`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/config/i18n.ts).

```js
export const availableLangs = {
  client: [
    Languages.English,
    Languages.Espanol,
    Languages.Chinese,
    Languages.ChineseTraditional,
    Languages.Italian,
    Languages.Portuguese,
    Languages.Ukrainian,
    Languages.Japanese,
    Languages.German,
    Languages.Arabic
  ],
  ...
};
```

Якщо ви тестуєте нову мову, створіть папку під назвою мови поряд з іншими мовами та скопіюйте файли JSON з іншої мови до нової папки.

Додайте нову мову до запису `Languages` та масиву `client` зверху файлу [`config/i18n.ts`](https://github.com/freeCodeCamp/freeCodeCamp/blob/main/config/i18n.ts).

Потім дотримуйтесь інструкцій з коментарів того ж файлу, щоб при потребі додати/оновити решту змінних.

Вкінці налаштуйте змінну `CLIENT_LOCALE` у файлі `.env` на рядок потрібної локалі із запису `Languages` у файлі вище.

## Як структурувати компоненти

Дотримуйтесь нижчеподаних вказівок, якщо працюєте над функціональністю чи помилкою клієнтського вебзастосунку (наприклад, додаєте елементи інтерфейсу користувача на сторінці налаштувань). Вони допоможуть підготувати компоненти для локалізації усіма підтримуваними мовами світу.

### Функціональний компонент

```js
import { useTranslation } from 'react-i18next';

// у методі render:
const { t } = useTranslation();

// виклик функції «t» з ключем із файлу JSON:
<p>{t('key')}</p>; // детальніше нижче
```

### Класовий компонент

```js
import { withTranslation } from 'react-i18next';

// withTranslation додає функцію «t» до пропсів:
const { t } = this.props;

// виклик функції «t» з ключем із файлу JSON:
<h1>{t('key')}</h1> // детальніше нижче

// експорт без redux:
export default withTranslation()(Component);

// або з redux:
export default connect(...)(withTranslation()(Component));
```

## Переклад із використанням функції «t»

### Звичайний переклад

```js
// у компоненті:
<p>{t('p1')}</p>

// у файлі JSON:
{
  "p1": "My paragraph"
}

// вивід:
<p>My paragraph</p>
```

### З динамічними даними

```js
// у компоненті:
const username = 'moT';

<p>{t('welcome', { username: username })}</p>

// у файлі JSON:
{
  "welcome": "Welcome {{username}}"
}

// вивід:
<p>Welcome moT</p>
```

Приклад вище передає об’єкт до функції `t` зі змінною `username`. Змінна буде використана у значенні JSON, де є `{{username}}`.

## Переклад із компонентом `Trans`

Загальне правило: використовуйте функцію «t», коли можете. Якщо того недостатньо, існує компонент `Trans` (зазвичай потрібен тоді, коли у тексті наявні вбудовані елементи). Ви можете використовувати компонент `Trans` з будь-яким типом компонента react.

### Звичайні вкладені елементи

```js
// у компоненті:
import { Trans } from 'react-i18next'

<p>
  <Trans>fcc.greeting</Trans>
</p>

// у файлі JSON:
{
  "fcc": {
    "greeting": "Welcome to <strong>freeCodeCamp</strong>"
  }
}

// вивід:
<p>Welcome to <strong>freeCodeCamp</strong></p>
```

Ви можете розміщувати ключі всередині тегів компоненту, як у прикладі вище, якщо текст містить «прості» теги без атрибутів. До тегів за замовчуванням належать `br`, `strong`, `i` та `p`, але цей список може бути більшим у конфігурації і18n.

### Складні вкладені елементи

В інших випадках ви захочете розмістити певний текст всередині іншого елемента. Тег посилання є хорошим прикладом:

```js
// у компоненті:
<p>
  <Trans i18nKey='check-forum'>
    <a href='https://forum.freecodecamp.org/'>placeholder</a>
  </Trans>
</p>

// у файлі JSON:
{
  "check-forum": "Check out <0>our forum</0>."
}

// вивід:
<p>Check out <a href='https://forum.freecodecamp.org/'>our forum</a></p>
```

У прикладі вище ключ розміщений в атрибутах компонента `Trans`. `<0>` та `</0>` у JSON представляють перший дочірній елемент компонента (у цьому разі елемент посилання). Якщо існує більше дочірніх елементів, то вони просто перераховуються, використовуючи такий самий синтаксис. Ви можете знайти дочірні елементи компонента в інструментах розробки react. `placeholder` наявний лише через те, що лінтер жалівся щодо порожніх елементів `<a>`.

### Зі змінною

```js
// у компоненті:
const email = 'team@freecodecamp.org';

<p>
  <Trans email={email} i18nKey='fcc.email'>
    <a href={`mailto:${email}`}>
      {{ email }}
    </a>
  </Trans>
</p>

// у файлі JSON:
{
  "fcc": {
    "email": "Send us an email at: <0>{{email}}</0>"
  }
}

// вивід:
<p>Send us an email at: <a href='mailto:team@freecodecamp.org'>team@freecodecamp.org</a><p>
```

У прикладі вище ключ та змінна розміщені в атрибутах компонента `Trans`. `{{ email }}` також повинен знаходитись у компоненті `Trans`, незалежно де саме.

## Зміна тексту

Щоб змінити текст клієнтської частини, перейдіть до відповідного файлу `.json`, знайдіть ключ, який використовується у компоненті React, та змініть його значення на новий текст. Шукайте цей ключ у кодовій базі, щоб переконатися, що він не використовується в іншому місці. Якщо використовується, переконайтесь, чи є сенс застосовувати зміни.

## Додавання тексту

Якщо текст, який ви хочете додати до клієнта, існує у відповідному файлі `.json`, використовуйте наявний ключ. В іншому випадку створіть новий ключ.

Файл англійською мовою є «джерелом правди» для всіх файлів `.json` з однаковою назвою. Якщо вам потрібно додати новий ключ, додайте його там. Потім додайте цей ключ до **усіх** файлів `translations.json`.

> [!NOTE] Використовуйте англомовний текст для усіх мов, якщо файл перекладається на Crowdin. Якщо ви цього не зробите, тести не пройдуть.

Також було б добре тримати усі ключі в одному порядку для усіх файлів. Спробуйте розставити всю пунктуацію, інтервали, лапки тощо у файлах JSON, а не у файлах компонента чи сервера.

> [!NOTE] Підкреслення (`_`) є зарезервованим знаком для ключів у файлах клієнтської частини. Див. [документацію](https://www.i18next.com/translation-function/plurals) про те, як вони використовуються.

## Запропонуйте запит на злиття (PR)

Як тільки ви затвердили свої зміни, див. [як відкрити запит на злиття](how-to-open-a-pull-request.md).

## Корисна документація

- [Документація react-i18next](https://react.i18next.com/latest/usetranslation-hook)
- [Документація i18next](https://www.i18next.com/translation-function/essentials)
