---
title: "Локалізація"
description: "Документація локалізації для платформи Ring"
locale: "uk"
---
# Локалізація та багатомовна підтримка

Ring Platform v1.6.0 використовує **env-driven locale config** і **next-intl** з `localePrefix: 'as-needed'`.

## Канонічна конфігурація (v1.6.0)

```bash
NEXT_PUBLIC_SUPPORTED_LOCALES=en,uk,ru
NEXT_PUBLIC_DEFAULT_LOCALE=en
```

Джерело правди: `lib/locale-config.ts`. Див. [Система локалей](/docs/features/locale-system.md).

**Навігація:** лише `Link`, `useRouter`, `usePathname` з `@/i18n/routing`. Перемикання: `persistRingLocalePreference()` + `replaceLocalePath()`.

## 🌍 Підтримувані мови

Платформа Ring підтримує наступні мови з повною локалізацією:

- **EN** (English) - Мова за замовчуванням
- **UK** (Українська) - Повна локалізація з культурною адаптацією
- **RU** (Русский) - Технічна документація та інтерфейс

### Майбутні мови
- **DE** (Deutsch) - Планується
- **FR** (Français) - Планується
- **ES** (Español) - Планується

## 📁 Структура файлів локалізації

### Організація файлів

```
locales/
├── en/                    # Англійська (джерельна)
│   ├── common.json        # Загальні рядки (кнопки, повідомлення)
│   ├── pages.json         # Сторінки та маршрути
│   ├── entities.json      # Сутності та компанії
│   ├── opportunities.json # Можливості та вакансії
│   ├── auth.json          # Аутентифікація
│   ├── wallet.json        # Гаманець та платежі
│   ├── navigation.json    # Навігація та меню
│   └── ...
├── uk/                    # Українська
│   ├── common.json
│   ├── pages.json
│   └── ...
└── ru/                    # Російська
    ├── common.json
    ├── pages.json
    └── ...
```

### Структура файлів локалізації

// locales/en/common.json

{`{
  "actions": {
    "save": "Save",
    "cancel": "Cancel",
    "delete": "Delete",
    "edit": "Edit",
    "create": "Create"
  },
  "messages": {
    "success": {
      "saved": "Successfully saved",
      "deleted": "Successfully deleted"
    },
    "errors": {
      "generic": "An error occurred",
      "network": "Network error"
    }
  },
  "validation": {
    "required": "This field is required",
    "email": "Please enter a valid email",
    "minLength": "Minimum length is {{count}} characters"
  }
}`}

// locales/uk/common.json

{`{
  "actions": {
    "save": "Зберегти",
    "cancel": "Скасувати",
    "delete": "Видалити",
    "edit": "Редагувати",
    "create": "Створити"
  },
  "messages": {
    "success": {
      "saved": "Успішно збережено",
      "deleted": "Успішно видалено"
    },
    "errors": {
      "generic": "Сталася помилка",
      "network": "Помилка мережі"
    }
  },
  "validation": {
    "required": "Це поле обов'язкове",
    "email": "Будь ласка, введіть дійсну електронну адресу",
    "minLength": "Мінімальна довжина {{count}} символів"
  }
}`}

## 🔧 Використання в коді

### Хуки локалізації

// components/MyComponent.tsx

{`'use client'

import { useTranslations } from 'next-intl'

export function MyComponent() {
  const t = useTranslations('common')

  return (
    
      {t('welcome.title')}
      
        {t('actions.save')}
      
      {t('messages.success.saved')}
    
  )
}`}

### Серверні компоненти

{`// app/(public)/[locale]/page.tsx
import { getTranslations } from 'next-intl/server'

export default async function HomePage() {
  const t = await getTranslations('pages.home')

  return (
    
      {t('title')}
      {t('description')}
    
  )
}`}

### Інтерполяція та плюралізація

// Підтримка параметрів

{`const message = t('validation.minLength', { count: 5 })

// Плюралізація
const itemsText = t('items.count', { count: items.length })

// locales/en/common.json
{
  "items": {
    "count": "No items | {count} item | {count} items"
  }
}

// locales/uk/common.json
{
  "items": {
    "count": "Немає елементів | {count} елемент | {count} елементів | {count} елемента"
  }
}`}

## 🗂️ Маршрутизація на основі локалей

### App Router конфігурація

// app/[locale]/layout.tsx

{`import { notFound } from 'next/navigation'
import { locales } from '@/lib/i18n'

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }))
}

export default function LocaleLayout({
  children,
  params: { locale }
}: {
  children: React.ReactNode
  params: { locale: string }
}) {
  // Перевірка валідності локалі
  if (!locales.includes(locale as any)) {
    notFound()
  }

  return (
    
      
        {children}
      
    
  )
}`}

### Middleware для маршрутизації

// middleware.ts

{`import createMiddleware from 'next-intl/middleware'

export default createMiddleware({
  locales: ['en', 'uk', 'ru'],
  defaultLocale: 'en',
  localePrefix: 'as-needed' // /uk/page, /page (en default)
})

export const config = {
  matcher: ['/((?!api|_next|_vercel|.*\\..*).*)']
}`}

## 🎨 Культурна адаптація

### Регіональні особливості

#### Українська (UK)
- **Дата та час**: `DD.MM.YYYY`, `HH:MM`
- **Валюта**: `₴` (гривня), `USD`, `EUR`
- **Формати чисел**: `1 234,56` (пробіл як роздільник, кома для десяткових)
- **Адреси**: Спочатку місто, потім вулиця

#### Російська (RU)
- **Дата та час**: `DD.MM.YYYY`, `HH:MM`
- **Валюта**: `₽` (рубль), `USD`, `EUR`
- **Формати чисел**: `1 234,56`
- **Адреси**: Федеральний стандарт

### Локалізований контент

// components/LocalizedContent.tsx

{`import { useLocale } from 'next-intl'

export function LocalizedContent() {
  const locale = useLocale()

  // Різний контент для різних регіонів
  const content = {
    en: {
      currency: 'USD',
      dateFormat: 'MM/DD/YYYY'
    },
    uk: {
      currency: 'UAH',
      dateFormat: 'DD.MM.YYYY'
    },
    ru: {
      currency: 'RUB',
      dateFormat: 'DD.MM.YYYY'
    }
  }

  return (
    
      Локальна валюта: {content[locale].currency}
      Формат дати: {content[locale].dateFormat}
    
  )
}`}

## 🔄 Додавання нової мови

### Крок 1: Створення файлів локалізації

Створення директорії для нової мови Копіювання базових файлів з англійської Переклад файлів (використовуйте професійних перекладачів) locales/de/common.json, locales/de/pages.json, etc.

{`mkdir -p locales/de

cp -r locales/en/* locales/de/`}

### Крок 2: Оновлення конфігурації

// lib/i18n.ts

{`export const locales = ['en', 'uk', 'ru', 'de'] as const
export const defaultLocale = 'en'

// middleware.ts
export default createMiddleware({
  locales: ['en', 'uk', 'ru', 'de'],
  defaultLocale: 'en'
})`}

### Крок 3: Тестування

// Перевірка всіх маршрутів з новою локаллю

{`const testUrls = [
  '/de',
  '/de/entities',
  '/de/opportunities',
  '/de/login'
]

// Перевірка fallback на англійську
const missingKeys = checkMissingTranslations('de')`}

## 🧪 Тестування локалізації

### Перевірка відсутніх ключів

// lib/test-i18n.ts

{`export function checkMissingTranslations(locale: string) {
  const english = loadLocale('en')
  const target = loadLocale(locale)

  const missing = []

  function checkKeys(enObj: any, targetObj: any, path = '') {
    for (const key in enObj) {
      const currentPath = path ? `${path}.${key}` : key

      if (typeof enObj[key] === 'object') {
        if (!targetObj || !targetObj[key]) {
          missing.push(currentPath)
        } else {
          checkKeys(enObj[key], targetObj[key], currentPath)
        }
      } else if (!targetObj || !targetObj[key]) {
        missing.push(currentPath)
      }
    }
  }

  checkKeys(english, target)
  return missing
}`}

### Інтеграційні тести

// __tests__/i18n.test.ts

{`describe('Internationalization', () => {
  test('all locales have consistent keys', () => {
    const locales = ['uk', 'ru']

    for (const locale of locales) {
      const missing = checkMissingTranslations(locale)
      expect(missing).toHaveLength(0)
    }
  })

  test('date formatting works in all locales', () => {
    const date = new Date('2023-10-16')

    expect(formatDate(date, 'en')).toBe('10/16/2023')
    expect(formatDate(date, 'uk')).toBe('16.10.2023')
    expect(formatDate(date, 'ru')).toBe('16.10.2023')
  })
})`}

## 🚀 Продуктивність та оптимізація

### Ледаче завантаження локалей

// lib/i18n-loader.ts

{`export async function loadLocale(locale: string) {
  try {
    const messages = await import(`@/locales/${locale}/common.json`)
    return messages.default
  } catch (error) {
    console.warn(`Failed to load locale ${locale}, falling back to English`)
    const fallback = await import('@/locales/en/common.json')
    return fallback.default
  }
}`}

### Кешування перекладів

// lib/translation-cache.ts

{`class TranslationCache {
  private cache = new Map<string, any>()

  get(locale: string, namespace: string) {
    const key = `${locale}:${namespace}`
    return this.cache.get(key)
  }

  set(locale: string, namespace: string, translations: any) {
    const key = `${locale}:${namespace}`
    this.cache.set(key, translations)
  }
}`}

## 📊 Моніторинг та аналітика

### Відстеження використання мов

// lib/analytics/locale-tracking.ts

{`export function trackLocaleUsage(locale: string, page: string) {
  // Відправка до аналітики
  analytics.track('locale_used', {
    locale,
    page,
    timestamp: new Date().toISOString()
  })
}`}

### Перевірка якості перекладів

// lib/i18n-validation.ts

{`export function validateTranslations(locale: string) {
  const translations = loadLocale(locale)

  const issues = []

  // Перевірка довжини рядків
  for (const [key, value] of Object.entries(translations)) {
    if (typeof value === 'string' && value.length > 100) {
      issues.push(`Long string: ${key}`)
    }
  }

  // Перевірка HTML у рядках
  for (const [key, value] of Object.entries(translations)) {
    if (typeof value === 'string' && /<\/?[a-z][\s\S]*>/i.test(value)) {
      issues.push(`HTML in translation: ${key}`)
    }
  }

  return issues
}`}

---

**🌐 Повна багатомовна підтримка**

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

*Потрібна допомога з локалізацією? Зверніться до нашої [документації розробки](https://ring-platform.org/docs/development/localization) або приєднайтеся до [спільноти перекладачів](https://discord.gg/ring-platform).*"
