diff --git a/README.md b/README.md
index 9424f027f..7a4a57702 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@
| **Workspaces** | Isolate each brand, client, or project in its own space. |
| **REST API + MCP** | Full programmatic control; AI assistants integrate natively. |
| **Native analytics** | Per-account reach and engagement across every connected platform. |
-| **Multi-language** | English, Spanish, and Portuguese. |
+| **Multi-language** | English, Ukrainian, Spanish, Portuguese, French, German, Italian, Dutch, Polish, Greek, Japanese, Korean, Chinese, Russian, Turkish, and Arabic. |
## Supported platforms
diff --git a/app/Enums/Workspace/ContentLanguage.php b/app/Enums/Workspace/ContentLanguage.php
index ecb7f3e24..190549af1 100644
--- a/app/Enums/Workspace/ContentLanguage.php
+++ b/app/Enums/Workspace/ContentLanguage.php
@@ -17,6 +17,7 @@
enum ContentLanguage: string
{
case English = 'en';
+ case Ukrainian = 'uk';
case PortugueseBrazil = 'pt-BR';
case Spanish = 'es';
case French = 'fr';
@@ -41,6 +42,7 @@ public function label(): string
{
return match ($this) {
self::English => 'English',
+ self::Ukrainian => 'Українська',
self::PortugueseBrazil => 'Português (Brasil)',
self::Spanish => 'Español',
self::French => 'Français',
@@ -66,6 +68,7 @@ public function englishName(): string
{
return match ($this) {
self::English => 'English',
+ self::Ukrainian => 'Ukrainian',
self::PortugueseBrazil => 'Brazilian Portuguese',
self::Spanish => 'Spanish',
self::French => 'French',
diff --git a/config/languages.php b/config/languages.php
index 37de477be..c019ef69d 100644
--- a/config/languages.php
+++ b/config/languages.php
@@ -17,6 +17,7 @@
'available' => [
'en' => 'English',
+ 'uk' => 'Українська',
'es' => 'Español',
'pt-BR' => 'Português',
'fr' => 'Français',
diff --git a/lang/uk/accounts.php b/lang/uk/accounts.php
new file mode 100644
index 000000000..a5f5b69fe
--- /dev/null
+++ b/lang/uk/accounts.php
@@ -0,0 +1,148 @@
+ 'Підключення',
+ 'page_title' => 'Соціальні акаунти',
+ 'description' => 'Огляд усіх підключених соціальних акаунтів',
+ 'connect_cta' => 'Підключити',
+
+ 'not_connected' => 'Не підключено',
+ 'connect' => 'Підключити',
+ 'connection_lost' => 'З’єднання втрачено',
+ 'reconnect' => 'Перепідключити',
+ 'reconnect_account' => 'Перепідключити акаунт',
+ 'view_profile' => 'Переглянути профіль',
+ 'disconnect' => 'Від’єднати',
+
+ 'descriptions' => [
+ 'linkedin' => 'Підключіть профіль LinkedIn або сторінку компанії',
+ 'linkedin-page' => 'Підключіть сторінку компанії LinkedIn',
+ 'x' => 'Підключіть акаунт X (Twitter)',
+ 'tiktok' => 'Підключіть акаунт TikTok',
+ 'youtube' => 'Підключіть канал YouTube',
+ 'facebook' => 'Підключіть сторінку Facebook',
+ 'instagram' => 'Підключіть професійний акаунт Instagram',
+ 'instagram-facebook' => 'Підключіть Instagram через сторінку Facebook',
+ 'threads' => 'Підключіть акаунт Threads',
+ 'pinterest' => 'Підключіть акаунт Pinterest',
+ 'bluesky' => 'Підключіть акаунт Bluesky',
+ 'mastodon' => 'Підключіть акаунт Mastodon',
+ 'telegram' => 'Підключіть канал або групу Telegram',
+ 'discord' => 'Підключіть сервер Discord',
+ ],
+
+ 'disconnect_modal' => [
+ 'title' => 'Від’єднати акаунт',
+ 'description' => 'Ви впевнені, що хочете від’єднати цей акаунт? Ви зможете підключити його знову будь-коли.',
+ 'confirm' => 'Від’єднати',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'bluesky' => [
+ 'title' => 'Підключити Bluesky',
+ 'description' => 'Введіть облікові дані для підключення',
+ 'email' => 'Email',
+ 'email_placeholder' => 'yourhandle.bsky.social',
+ 'app_password' => 'Пароль додатка',
+ 'app_password_placeholder' => 'xxxx-xxxx-xxxx-xxxx',
+ 'app_password_hint' => 'З міркувань безпеки використовуйте пароль додатка. Створіть його на bsky.app/settings.',
+ 'submit' => 'Підключити Bluesky',
+ 'submitting' => 'Підключення...',
+ ],
+
+ 'mastodon' => [
+ 'title' => 'Підключити Mastodon',
+ 'description' => 'Введіть інстанс Mastodon',
+ 'instance_url' => 'URL інстансу',
+ 'instance_placeholder' => 'https://mastodon.social',
+ 'instance_hint' => 'Введіть URL інстансу Mastodon (напр. mastodon.social, techhub.social)',
+ 'submit' => 'Продовжити з Mastodon',
+ 'submitting' => 'Підключення...',
+ ],
+
+ 'telegram' => [
+ 'title' => 'Підключити Telegram',
+ 'description' => 'Під’єднайте канал або групу',
+ 'step_admin' => 'Додайте :bot як адміністратора до каналу або групи Telegram.',
+ 'step_command' => 'Опублікуйте цю команду в каналі або групі:',
+ 'waiting' => 'Очікуємо підключення каналу…',
+ 'connected' => 'Канал підключено!',
+ 'connected_toast' => 'Канал Telegram успішно підключено!',
+ 'copied_toast' => 'Команду скопійовано в буфер обміну',
+ 'copy_tooltip' => 'Копіювати команду',
+ 'expired' => 'Цей код прострочено. Згенеруйте новий і спробуйте ще раз.',
+ 'new_code' => 'Згенерувати новий код',
+ 'retry' => 'Спробувати ще раз',
+ 'error_generic' => 'Не вдалося розпочати підключення. Спробуйте ще раз.',
+ 'network_taken' => 'У цьому робочому просторі вже підключено канал Telegram. Спочатку від’єднайте його.',
+ ],
+
+ 'facebook' => [
+ 'title' => 'Виберіть сторінку Facebook',
+ 'description' => 'Виберіть сторінку, яку хочете підключити',
+ 'no_pages' => 'Сторінок не знайдено',
+ 'no_pages_description' => 'Ви не є адміністратором жодної сторінки Facebook.',
+ 'page_label' => 'Сторінка Facebook',
+ 'view' => 'Переглянути',
+ 'choose' => 'Вибрати',
+ ],
+
+ 'instagram_facebook' => [
+ 'title' => 'Виберіть акаунт Instagram',
+ 'description' => 'Виберіть акаунт Instagram, який хочете підключити',
+ 'no_pages' => 'Акаунтів Instagram не знайдено',
+ 'no_pages_description' => 'Не знайдено сторінок Facebook із підключеними бізнес-акаунтами Instagram.',
+ 'view' => 'Переглянути',
+ 'choose' => 'Вибрати',
+ ],
+
+ 'linkedin' => [
+ 'title' => 'Виберіть сторінку LinkedIn',
+ 'description' => 'Виберіть сторінку, яку хочете підключити',
+ 'no_pages' => 'Сторінок не знайдено',
+ 'no_pages_description' => 'Ви не є адміністратором жодної сторінки LinkedIn.',
+ 'page_label' => 'Сторінка LinkedIn',
+ 'select_title' => 'Де ви хочете публікувати?',
+ 'select_subtitle' => 'Публікуйте від свого імені або виберіть сторінку компанії, якою керуєте.',
+ 'person_tag' => 'Особа',
+ 'organization_tag' => 'Організація',
+ 'view' => 'Переглянути',
+ 'choose' => 'Вибрати',
+ ],
+
+ 'flash' => [
+ 'disconnected' => 'Акаунт успішно від’єднано!',
+ 'connected' => 'Акаунт успішно підключено!',
+ 'session_expired' => 'Сесію завершено. Спробуйте ще раз.',
+ 'workspace_not_found' => 'Робочий простір не знайдено.',
+ 'activated' => 'Акаунт активовано!',
+ 'deactivated' => 'Акаунт деактивовано!',
+ 'already_connected' => 'Ця платформа вже підключена.',
+ 'no_youtube_channels' => 'Каналів YouTube не знайдено. Спочатку створіть канал.',
+ ],
+
+ 'popup_callback' => [
+ 'title_success' => 'Підключено',
+ 'title_error' => 'Помилка',
+ 'closing' => 'Це вікно закриється автоматично...',
+ 'manual_close' => 'Ви можете закрити це вікно.',
+ 'popup_blocked' => 'Не вдалося відкрити вікно підключення. Дозвольте спливаючі вікна і спробуйте ще раз.',
+ 'connected' => 'Акаунт підключено!',
+ 'reconnected' => 'Акаунт перепідключено!',
+ 'error_connecting' => 'Помилка підключення акаунта. Спробуйте ще раз.',
+ 'network_taken' => 'У цьому робочому просторі вже є акаунт для цієї мережі. Спочатку від’єднайте його.',
+ 'error_connecting_page' => 'Помилка підключення сторінки. Спробуйте ще раз.',
+ 'error_connecting_channel' => 'Помилка підключення каналу. Спробуйте ще раз.',
+ 'session_expired' => 'Сесію завершено. Спробуйте ще раз.',
+ 'workspace_not_found' => 'Робочий простір не знайдено.',
+ 'invalid_state' => 'Недійсний стан. Спробуйте ще раз.',
+ 'failed_to_authenticate' => 'Не вдалося автентифікуватися.',
+ 'failed_to_get_profile' => 'Не вдалося отримати профіль.',
+ 'page_not_found' => 'Сторінку не знайдено.',
+ 'channel_not_found' => 'Канал не знайдено.',
+ 'no_facebook_pages' => 'Сторінок Facebook не знайдено. Ви маєте бути адміністратором хоча б однієї сторінки.',
+ 'no_facebook_instagram_pages' => 'Не знайдено сторінок Facebook із підключеними акаунтами Instagram.',
+ 'no_youtube_channels' => 'Каналів YouTube не знайдено. Спочатку створіть канал.',
+ 'not_linkedin_admin' => 'Ви не є адміністратором жодної сторінки LinkedIn.',
+ ],
+];
diff --git a/lang/uk/analytics.php b/lang/uk/analytics.php
new file mode 100644
index 000000000..1a4891b22
--- /dev/null
+++ b/lang/uk/analytics.php
@@ -0,0 +1,55 @@
+ 'Немає підключених акаунтів з аналітикою.',
+ 'no_accounts_match' => 'Жоден акаунт не відповідає.',
+ 'search_account' => 'Пошук акаунта…',
+ 'select_account' => 'Виберіть акаунт, щоб переглянути аналітику.',
+ 'no_data' => 'Дані аналітики недоступні.',
+
+ 'metrics' => [
+ 'avg_view_duration' => 'Сер. тривалість перегляду (с)',
+ 'avg_view_percentage' => 'Сер. відсоток перегляду',
+ 'bookmarks' => 'Закладки',
+ 'clicks' => 'Кліки',
+ 'comments' => 'Коментарі',
+ 'custom_reaction' => 'Власна',
+ 'engagement' => 'Залучення',
+ 'favourites' => 'Обране',
+ 'followers' => 'Підписники',
+ 'following' => 'Підписки',
+ 'impressions' => 'Покази',
+ 'interactions' => 'Взаємодії',
+ 'likes' => 'Вподобання',
+ 'members' => 'Учасники',
+ 'minutes_watched' => 'Хвилин переглянуто',
+ 'organic_followers' => 'Органічні підписники',
+ 'outbound_clicks' => 'Зовнішні кліки',
+ 'page_followers' => 'Підписники сторінки',
+ 'page_reach' => 'Охоплення сторінки',
+ 'page_views' => 'Перегляди сторінки',
+ 'paid_followers' => 'Платні підписники',
+ 'pin_click_rate' => 'CTR піна',
+ 'pin_clicks' => 'Кліки по піну',
+ 'posts_engagement' => 'Залучення постів',
+ 'posts_reach' => 'Охоплення постів',
+ 'quotes' => 'Цитати',
+ 'reach' => 'Охоплення',
+ 'reblogs' => 'Репости',
+ 'recent_comments' => 'Нещодавні коментарі',
+ 'recent_likes' => 'Нещодавні вподобання',
+ 'recent_shares' => 'Нещодавні поширення',
+ 'replies' => 'Відповіді',
+ 'reposts' => 'Репости',
+ 'retweets' => 'Ретвіти',
+ 'saves' => 'Збереження',
+ 'shares' => 'Поширення',
+ 'subscribers' => 'Підписники',
+ 'subscribers_gained' => 'Отримано підписників',
+ 'subscribers_lost' => 'Втрачено підписників',
+ 'total_likes' => 'Усього вподобань',
+ 'video_views' => 'Перегляди відео',
+ 'videos' => 'Відео',
+ 'views' => 'Перегляди',
+ ],
+];
diff --git a/lang/uk/assets.php b/lang/uk/assets.php
new file mode 100644
index 000000000..774f7d5f9
--- /dev/null
+++ b/lang/uk/assets.php
@@ -0,0 +1,52 @@
+ 'Медіафайли',
+
+ 'tabs' => [
+ 'my_uploads' => 'Мої завантаження',
+ 'stock_photos' => 'Стокові фото',
+ 'gifs' => 'GIF',
+ ],
+
+ 'upload' => [
+ 'drag_drop' => 'Перетягніть файли сюди або натисніть, щоб вибрати',
+ 'formats' => 'JPEG, PNG, GIF, WebP, MP4, PDF',
+ 'uploading' => 'Завантаження...',
+ 'failed' => 'Не вдалося завантажити :file. Спробуйте ще раз.',
+ ],
+
+ 'empty' => [
+ 'title' => 'Медіафайлів ще немає',
+ 'description' => 'Завантажуйте зображення та відео, щоб створити медіатеку.',
+ ],
+
+ 'save_to_assets' => 'Зберегти в медіафайли',
+ 'saved' => 'Збережено у ваші медіафайли!',
+ 'create_post' => 'Створити пост',
+ 'add_to_post' => 'Додати до поста',
+ 'search_placeholder' => 'Пошук медіа...',
+
+ 'delete' => [
+ 'title' => 'Видалити медіафайл',
+ 'description' => 'Ви впевнені, що хочете видалити цей медіафайл? Цю дію не можна скасувати.',
+ 'confirm' => 'Видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'unsplash' => [
+ 'search_placeholder' => 'Пошук безкоштовних фото...',
+ 'no_results' => 'Фото не знайдено',
+ 'no_results_description' => 'Спробуйте інший пошуковий запит.',
+ 'trending' => 'Популярне на Unsplash',
+ 'start_searching' => 'Шукайте безкоштовні стокові фото з Unsplash',
+ ],
+
+ 'giphy' => [
+ 'trending' => 'Популярне на Giphy',
+ 'search_placeholder' => 'Пошук GIF...',
+ 'no_results' => 'GIF не знайдено',
+ 'no_results_description' => 'Спробуйте інший пошуковий запит.',
+ 'powered_by' => 'Працює на GIPHY',
+ ],
+];
diff --git a/lang/uk/auth.php b/lang/uk/auth.php
new file mode 100644
index 000000000..549cd961f
--- /dev/null
+++ b/lang/uk/auth.php
@@ -0,0 +1,144 @@
+ 'Ці облікові дані не відповідають нашим записам.',
+ 'password' => 'Наданий пароль неправильний.',
+ 'throttle' => 'Забагато спроб входу. Спробуйте ще раз через :seconds сек.',
+
+ 'flash' => [
+ 'welcome' => 'Ласкаво просимо до TryPost!',
+ 'welcome_trial' => 'Ласкаво просимо до TryPost! Ваш пробний період розпочато.',
+ ],
+
+ 'legal' => 'Продовжуючи, ви погоджуєтеся з нашими Умовами використання та Політикою конфіденційності.',
+
+ 'slides' => [
+ 'calendar' => [
+ 'title' => 'Візуальний календар',
+ 'description' => 'Плануйте та плануйте контент за допомогою інтуїтивного календаря з перетягуванням для всіх ваших соціальних акаунтів.',
+ ],
+ 'scheduling' => [
+ 'title' => 'Розумне планування',
+ 'description' => 'Плануйте пости в LinkedIn, X, Instagram, TikTok, YouTube та інших — усе в одному місці.',
+ ],
+ 'media' => [
+ 'title' => 'Багатий медіаконтент',
+ 'description' => 'Публікуйте зображення, каруселі, stories та reels. Кожна платформа автоматично отримує правильний формат.',
+ ],
+ 'video' => [
+ 'title' => 'Публікація відео',
+ 'description' => 'Завантажуйте відео один раз і публікуйте в TikTok, YouTube Shorts, Instagram Reels та Facebook Reels.',
+ ],
+ 'team' => [
+ 'title' => 'Командні робочі простори',
+ 'description' => 'Запрошуйте команду, призначайте ролі та керуйте кількома брендами в окремих робочих просторах.',
+ ],
+ 'signatures' => [
+ 'title' => 'Підписи',
+ 'description' => 'Зберігайте багаторазові підписи (хештеги, посилання, підписи) і додавайте їх до постів одним кліком.',
+ ],
+ ],
+
+ 'or_continue_with' => 'Або продовжити через',
+ 'or_continue_with_email' => 'Або продовжити через email',
+ 'google_login' => 'Увійти через Google',
+ 'google_signup' => 'Зареєструватися через Google',
+ 'github_login' => 'Увійти через GitHub',
+ 'github_signup' => 'Зареєструватися через GitHub',
+ 'github_email_unavailable' => 'Не вдалося отримати ваш email з GitHub. Зробіть email публічним або надайте доступ до email, потім спробуйте ще раз.',
+
+ 'signup_success' => [
+ 'page_title' => 'Ласкаво просимо',
+ 'title' => 'Налаштовуємо ваш обліковий запис',
+ 'description' => 'Зазвичай це займає лише кілька секунд...',
+ ],
+
+ 'login' => [
+ 'title' => 'Увійдіть до облікового запису',
+ 'description' => 'Введіть email і пароль нижче, щоб увійти',
+ 'page_title' => 'Вхід',
+ 'email' => 'Адреса email',
+ 'password' => 'Пароль',
+ 'forgot_password' => 'Забули пароль?',
+ 'remember_me' => 'Запам’ятати мене',
+ 'submit' => 'Увійти',
+ 'no_account' => 'Немає облікового запису?',
+ 'sign_up' => 'Зареєструватися',
+ ],
+
+ 'register' => [
+ 'title' => 'Увесь ваш соціальний календар в одному місці',
+ 'description' => 'Створіть обліковий запис і почніть планувати пости в усіх мережах.',
+ 'page_title' => 'Реєстрація',
+ 'signup_with_email' => 'Зареєструватися через email',
+ 'name' => 'Ім’я',
+ 'name_placeholder' => 'Повне ім’я',
+ 'email' => 'Адреса email',
+ 'password' => 'Пароль',
+ 'show_password' => 'Показати пароль',
+ 'hide_password' => 'Приховати пароль',
+ 'submit' => 'Створити обліковий запис',
+ 'has_account' => 'Уже маєте обліковий запис?',
+ 'log_in' => 'Увійти',
+ ],
+
+ 'forgot_password' => [
+ 'title' => 'Забули пароль',
+ 'description' => 'Введіть email, щоб отримати посилання для скидання пароля',
+ 'page_title' => 'Забули пароль',
+ 'email' => 'Адреса email',
+ 'submit' => 'Надіслати посилання для скидання',
+ 'return_to' => 'Або повернутися до',
+ 'log_in' => 'входу',
+ ],
+
+ 'reset_password' => [
+ 'title' => 'Скинути пароль',
+ 'description' => 'Введіть новий пароль нижче',
+ 'page_title' => 'Скинути пароль',
+ 'email' => 'Email',
+ 'password' => 'Пароль',
+ 'confirm_password' => 'Підтвердіть пароль',
+ 'confirm_placeholder' => 'Підтвердіть пароль',
+ 'submit' => 'Скинути пароль',
+ ],
+
+ 'verify_email' => [
+ 'title' => 'Підтвердіть email',
+ 'description' => 'Підтвердіть адресу email, натиснувши на посилання, яке ми щойно надіслали.',
+ 'page_title' => 'Підтвердження email',
+ 'link_sent' => 'Нове посилання для підтвердження надіслано на email, який ви вказали під час реєстрації.',
+ 'resend' => 'Надіслати лист підтвердження ще раз',
+ 'log_out' => 'Вийти',
+ ],
+
+ 'accept_invite' => [
+ 'page_title' => 'Прийняти запрошення',
+ 'title' => 'Вас запросили!',
+ 'description' => 'Вас запросили приєднатися до робочого простору :workspace.',
+ 'workspace' => 'Робочий простір',
+ 'your_role' => 'Ваша роль',
+ 'email' => 'Email',
+ 'accept' => 'Прийняти запрошення',
+ 'decline' => 'Відхилити запрошення',
+ 'login_prompt' => 'Увійдіть або створіть обліковий запис, щоб прийняти це запрошення.',
+ 'log_in' => 'Увійти',
+ 'create_account' => 'Створити обліковий запис',
+ 'expired_title' => 'Це запрошення більше не дійсне',
+ 'expired_description' => 'Робочий простір для цього запрошення було видалено. Попросіть власника облікового запису надіслати нове запрошення, якщо вам ще потрібен доступ.',
+ 'expired_action' => 'На головну',
+ ],
+
+];
diff --git a/lang/uk/automations.php b/lang/uk/automations.php
new file mode 100644
index 000000000..4e1f8df73
--- /dev/null
+++ b/lang/uk/automations.php
@@ -0,0 +1,413 @@
+ 'Редактор автоматизацій найкраще працює на великому екрані. Відкрийте його на комп’ютері, щоб побудувати workflow.',
+ 'title' => 'Автоматизації',
+ 'default_name' => 'Нова автоматизація',
+
+ 'actions' => [
+ 'new' => 'Нова автоматизація',
+ 'edit' => 'Редагувати',
+ 'save' => 'Зберегти',
+ 'activate' => 'Активувати',
+ 'pause' => 'Призупинити',
+ 'delete' => 'Видалити',
+ 'retry' => 'Повторити',
+ 'guide' => 'Дізнатися, як це працює',
+ ],
+
+ 'tabs' => [
+ 'build' => 'Конструктор',
+ 'variables' => 'Змінні',
+ 'test' => 'Тест',
+ ],
+
+ 'nav' => [
+ 'workflow' => 'Процес',
+ 'invocations' => 'Запуски',
+ 'metrics' => 'Метрики',
+ 'settings' => 'Налаштування',
+ ],
+
+ 'settings' => [
+ 'general' => 'Загальні',
+ 'general_description' => 'Перейменуйте цю автоматизацію.',
+ 'name_label' => 'Назва',
+ 'name_saved' => 'Автоматизацію перейменовано.',
+ 'status_title' => 'Статус',
+ 'status_description' => 'Активуйте, щоб запустити, або призупиніть, щоб зупинити.',
+ 'activated_at' => 'Активовано :date',
+ 'paused_at' => 'Призупинено :date',
+ 'created_at' => 'Створено :date',
+ 'danger_title' => 'Небезпечна зона',
+ 'danger_description' => 'Незворотні дії.',
+ 'delete_title' => 'Видалити цю автоматизацію',
+ 'delete_description' => 'Назавжди видаляє автоматизацію та історію її запусків.',
+ ],
+
+ 'status_run' => [
+ 'pending' => 'Очікує',
+ 'running' => 'Виконується',
+ 'waiting' => 'Очікування',
+ 'completed' => 'Завершено',
+ 'failed' => 'Помилка',
+ 'cancelled' => 'Скасовано',
+ ],
+
+ 'node_type' => [
+ 'trigger' => 'Тригер',
+ 'generate' => 'Генерація контенту',
+ 'delay' => 'Затримка',
+ 'condition' => 'Умова',
+ 'publish' => 'Публікація',
+ 'webhook' => 'Webhook',
+ 'end' => 'Кінець',
+ 'fetch_rss' => 'Отримати RSS',
+ 'http_request' => 'HTTP-запит',
+ ],
+
+ 'invocations' => [
+ 'empty' => 'Запусків ще немає.',
+ 'refresh' => 'Оновити',
+ 'search_placeholder' => 'Пошук за ID запуску…',
+ 'copied' => 'ID запуску скопійовано.',
+ 'loading' => 'Завантаження кроків…',
+ 'no_steps' => 'Кроки не записані.',
+ 'load_error' => 'Не вдалося завантажити кроки.',
+ 'steps' => '{0}Немає кроків|{1}:count крок|[2,*]:count кроки',
+ 'filter' => [
+ 'all' => 'Усі статуси',
+ ],
+ 'columns' => [
+ 'timestamp' => 'Час',
+ 'run' => 'Запуск',
+ 'status' => 'Статус',
+ 'message' => 'Останнє повідомлення',
+ 'duration' => 'Тривалість',
+ ],
+ 'summary' => [
+ 'completed' => 'Процес завершено',
+ 'failed' => 'Процес завершився з помилкою',
+ 'running' => 'Процес виконується',
+ 'cancelled' => 'Процес скасовано',
+ 'pending' => 'Процес очікує',
+ ],
+ ],
+
+ 'metrics' => [
+ 'overview' => 'Огляд',
+ 'runs_over_time' => 'Запуски з часом',
+ 'posts_by_platform' => 'Пости за платформами',
+ 'no_posts' => 'За цей період не опубліковано жодного поста.',
+ 'cards' => [
+ 'runs' => 'Усього запусків',
+ 'completed' => 'Завершено',
+ 'failed' => 'З помилкою',
+ 'in_progress' => 'В процесі',
+ 'success_rate' => 'Частка успішних',
+ 'avg_duration' => 'Сер. тривалість',
+ 'posts_created' => 'Створено постів',
+ ],
+ 'legend' => [
+ 'started' => 'Розпочато',
+ 'completed' => 'Завершено',
+ 'failed' => 'З помилкою',
+ ],
+ ],
+
+ 'categories' => [
+ 'sources' => 'Джерела',
+ 'content' => 'Контент',
+ 'flow' => 'Логіка',
+ 'output' => 'Вивід',
+ ],
+
+ 'variables' => [
+ 'title' => 'Змінні процесу',
+ 'hint' => 'Багаторазові значення, доступні скрізь через {{ variables.KEY }}. Зберігаються в зашифрованому вигляді.',
+ 'empty' => 'Змінних ще немає.',
+ 'key' => 'Ключ',
+ 'value' => 'Значення',
+ 'key_placeholder' => 'API_KEY',
+ 'value_placeholder' => 'Значення',
+ 'add' => 'Нова змінна',
+ ],
+
+ 'expr' => [
+ 'trigger_event' => 'Назва події тригера',
+ 'trigger_fired_at' => 'Коли спрацював тригер',
+ 'trigger_post_id' => 'ID поста-тригера',
+ 'trigger_post_content' => 'Вміст поста-тригера',
+ 'trigger_post_status' => 'Статус поста-тригера',
+ 'trigger_post_scheduled_at' => 'Коли пост заплановано',
+ 'trigger_post_published_at' => 'Коли пост опубліковано',
+ 'fetched_title' => 'Заголовок отриманого елемента',
+ 'fetched_link' => 'Посилання отриманого елемента',
+ 'fetched_date' => 'Дата публікації отриманого елемента',
+ 'fetched_content' => 'Повний вміст отриманого елемента',
+ 'fetched_description' => 'Короткий опис отриманого елемента',
+ 'fetched_author' => 'Автор отриманого елемента',
+ 'fetched_image' => 'URL зображення отриманого елемента',
+ 'fetched_categories' => 'Категорії отриманого елемента',
+ 'fetched_enclosure' => 'Медіа отриманого елемента (аудіо/відео/файл)',
+ 'fetched_pubdate' => 'Дата публікації отриманого елемента',
+ 'fetched_http' => 'Отриманий HTTP-елемент (додайте поле)',
+ 'generated_content' => 'AI-згенерований вміст поста',
+ 'generated_post_url' => 'URL AI-згенерованого поста',
+ 'variable' => 'Змінна процесу',
+ 'now' => 'Поточна дата та час',
+ ],
+
+ 'test' => [
+ 'description' => 'Виконує автоматизацію від початку до кінця, використовуючи синтезовані дані тригера. Корисно для перевірки кожного вузла без очікування реального розкладу або стрічки.',
+ 'starting' => 'Запуск тестового прогону…',
+ 'in_progress' => 'В процесі',
+ 'completed' => 'Завершено',
+ 'failed' => 'Помилка',
+ 'waiting' => 'Очікування',
+ 'close' => 'Закрити',
+ 'no_node_runs' => 'Очікування запуску першого вузла…',
+ 'node_input' => 'Вхід',
+ 'node_output' => 'Вихід',
+ 'node_error' => 'Помилка',
+ 'no_new_items' => 'Немає нових елементів — наступні вузли не запускалися.',
+ 'error_starting' => 'Не вдалося запустити тестовий прогін.',
+ 'with_real_data' => 'З реальними даними',
+ 'run' => 'Запустити тест',
+ 'idle_hint' => 'Натисніть «Запустити тест», щоб виконати автоматизацію від початку до кінця.',
+ 'real_data_hint' => 'Цей тест опублікує пости, оновить мітки опитування та викличе зовнішні побічні ефекти.',
+ 'dry_badge' => 'Тестовий прогін',
+ ],
+
+ 'status' => [
+ 'draft' => 'Чернетка',
+ 'active' => 'Активна',
+ 'paused' => 'Призупинена',
+ ],
+
+ 'index' => [
+ 'empty_title' => 'Автоматизацій ще немає',
+ 'empty_description' => 'Створіть першу автоматизацію, щоб публікувати на автопілоті.',
+ 'columns' => [
+ 'name' => 'Назва',
+ 'status' => 'Статус',
+ 'created' => 'Створено',
+ ],
+ ],
+
+ 'form' => [
+ 'activate_error_fallback' => 'Не вдалося активувати автоматизацію.',
+ 'pause_error_fallback' => 'Не вдалося призупинити автоматизацію.',
+ 'save_error_fallback' => 'Не вдалося зберегти автоматизацію.',
+ 'save_success' => 'Автоматизацію збережено.',
+ 'empty_canvas_title' => 'Почніть створювати автоматизацію',
+ 'empty_canvas_description' => 'Перетягніть вузол з лівої панелі, щоб почати.',
+ 'name_placeholder' => 'Автоматизація без назви',
+ ],
+
+ 'nodes' => [
+ 'trigger' => 'Тригер',
+ 'generate' => 'Генерація',
+ 'delay' => 'Затримка',
+ 'condition' => 'Умова',
+ 'publish' => 'Публікація',
+ 'webhook' => 'Webhook',
+ 'end' => 'Кінець',
+ 'end_summary' => 'Зупиняє автоматизацію тут',
+ 'fetch_rss' => 'Отримати RSS',
+ 'http_request' => 'HTTP-запит',
+ 'handles' => [
+ 'items' => 'є елементи',
+ 'no_items' => 'немає елементів',
+ ],
+ ],
+
+ 'config' => [
+ 'select_placeholder' => 'Виберіть…',
+ 'invalid_json' => 'Це ще не коректний JSON.',
+ 'expand_editor' => 'Розгорнути редактор',
+ 'minimize_editor' => 'Згорнути',
+
+ 'trigger' => [
+ 'type' => 'Тип тригера',
+ 'types' => [
+ 'schedule' => 'Розклад',
+ 'post_published' => 'Коли пост опубліковано',
+ 'post_scheduled' => 'Коли пост заплановано',
+ ],
+ 'post_published_hint' => 'Запускається щоразу, коли будь-який пост у цьому робочому просторі опубліковано. Опублікований пост доступний у {{ trigger.post }} для наступних вузлів.',
+ 'post_scheduled_hint' => 'Запускається щоразу, коли будь-який пост у цьому робочому просторі заплановано. Запланований пост доступний у {{ trigger.post }}.',
+
+ 'schedule' => [
+ 'field' => 'Інтервал спрацювання',
+ 'fields' => [
+ 'minutes' => 'Хвилини',
+ 'hours' => 'Години',
+ 'days' => 'Дні',
+ 'weeks' => 'Тижні',
+ 'months' => 'Місяці',
+ ],
+ 'minutes_interval' => 'Хвилин між спрацюваннями',
+ 'hours_interval' => 'Годин між спрацюваннями',
+ 'days_interval' => 'Днів між спрацюваннями',
+ 'hour' => 'Спрацювання о годині',
+ 'minute' => 'Спрацювання на хвилині',
+ 'weekdays' => 'Спрацювання у будні',
+ 'day_of_month' => 'День місяця',
+ 'weekday_names' => [
+ 'sun' => 'Нд',
+ 'mon' => 'Пн',
+ 'tue' => 'Вт',
+ 'wed' => 'Ср',
+ 'thu' => 'Чт',
+ 'fri' => 'Пт',
+ 'sat' => 'Сб',
+ ],
+ 'summary' => [
+ 'every_n_minutes' => 'Запускається щохвилини|Запускається кожні :count хвилини|Запускається кожні :count хвилин',
+ 'every_n_hours' => 'Запускається щогодини на :minute-й хвилині|Запускається кожні :count години на :minute-й хвилині|Запускається кожні :count годин на :minute-й хвилині',
+ 'every_n_days' => 'Запускається щодня о :time|Запускається кожні :count дні о :time|Запускається кожні :count днів о :time',
+ 'weekly' => 'Запускається у :days о :time',
+ 'monthly' => 'Запускається :day числа кожного місяця о :time',
+ ],
+ ],
+ ],
+ 'generate' => [
+ 'social_accounts' => 'Соціальні акаунти',
+ 'social_accounts_empty' => 'Немає підключених соціальних акаунтів. Спочатку підключіть один.',
+ 'target_slide_count' => 'Слайдів для генерації',
+ 'prompt_template' => 'Шаблон промпту',
+ 'prompt_template_hint' => 'Введіть {{, щоб вставити дані з попередніх кроків.',
+ 'image_count' => 'Зображень для генерації',
+ 'image_count_hint' => '0 = лише текст (без зображення). 1 = одне зображення. 2+ = карусель.',
+ 'use_brand_voice' => 'Використовувати голос бренду',
+ 'use_brand_voice_hint' => 'Застосовуйте опис і голос вашого бренду. Вимкніть для точної курації сторонніх джерел (новини, RSS).',
+ 'use_brand_visuals' => 'Використовувати візуал бренду',
+ 'use_brand_visuals_hint' => 'Спрямовуйте AI-зображення кольорами та айдентикою бренду. Вимкніть для нейтральних зображень лише за темою поста.',
+ 'style' => 'Стиль',
+ 'account_summary' => ':count акаунт · :format|:count акаунти · :format|:count акаунтів · :format',
+ 'formats' => [
+ 'single' => 'один',
+ 'carousel' => 'карусель',
+ ],
+ ],
+ 'delay' => [
+ 'duration' => 'Тривалість',
+ 'unit' => 'Одиниця',
+ 'units' => [
+ 'minutes' => 'Хвилини',
+ 'hours' => 'Години',
+ 'days' => 'Дні',
+ ],
+ ],
+ 'condition' => [
+ 'field' => 'Поле',
+ 'operator' => 'Оператор',
+ 'operators' => [
+ 'contains' => 'містить',
+ 'not_contains' => 'не містить',
+ 'equals' => 'дорівнює',
+ 'not_equals' => 'не дорівнює',
+ 'matches' => 'відповідає (regex)',
+ 'greater_than' => 'більше ніж',
+ 'less_than' => 'менше ніж',
+ ],
+ 'value' => 'Значення',
+ ],
+ 'publish' => [
+ 'mode' => 'Режим',
+ 'modes' => [
+ 'now' => 'Опублікувати зараз',
+ 'scheduled' => 'Запланувати',
+ 'draft' => 'Зберегти як чернетку',
+ ],
+ 'scheduled_offset' => 'Зміщення від тригера (хвилини)',
+ 'offset_summary' => ':mode · +:offset хв',
+ ],
+ 'webhook' => [
+ 'url' => 'URL',
+ 'method' => 'Метод',
+ 'payload_template' => 'Шаблон payload (JSON)',
+ ],
+ 'end' => [
+ 'reason' => 'Причина (необов’язково)',
+ 'reason_placeholder' => 'напр. Відфільтровано умовою',
+ ],
+ 'fetch_rss' => [
+ 'feed_url' => 'URL стрічки',
+ 'feed_url_hint' => 'При першому запуску мітку встановлюють на «зараз», щоб історичні елементи не заповнили наступні вузли. Наступні запуски бачать лише елементи новіші за попереднє опитування.',
+ 'inspect' => 'Перевірити стрічку',
+ 'inspecting' => 'Перевірка…',
+ 'inspect_hint' => 'Отримайте зразок, щоб дізнатися доступні поля для наступних вузлів.',
+ 'inspect_error' => 'Не вдалося прочитати цю стрічку. Перевірте URL і спробуйте ще раз.',
+ 'discovered_fields' => 'Доступні поля',
+ 'discovered_empty' => 'У останньому елементі поля не знайдено.',
+ ],
+ 'http_request' => [
+ 'url' => 'URL',
+ 'method' => 'Метод',
+ 'auth_type' => 'Автентифікація',
+ 'auth' => [
+ 'none' => 'Немає (публічний)',
+ 'bearer' => 'Bearer-токен',
+ 'basic' => 'Basic-автентифікація',
+ 'api_key' => 'Заголовок з API-ключем',
+ ],
+ 'bearer_token' => 'Bearer-токен',
+ 'basic_username' => 'Ім’я користувача',
+ 'basic_password' => 'Пароль',
+ 'api_key_header' => 'Назва заголовка',
+ 'api_key_value' => 'API-ключ',
+ 'body_template' => 'Шаблон тіла (JSON)',
+ 'headers' => 'Заголовки',
+ 'header_name' => 'Назва заголовка',
+ 'header_value' => 'Значення',
+ 'add_header' => 'Додати заголовок',
+ 'polling_section' => 'Список і дедуплікація (необов’язково)',
+ 'polling_hint' => 'Якщо відповідь — список, кожен елемент запускає процес окремо. Один об’єкт запускає його один раз.',
+ 'items_path' => 'Шлях до елементів',
+ 'items_path_hint' => 'Залиште порожнім, якщо відповідь уже масив. Використовуйте шлях через крапку (напр. data.items) для вкладеного масиву або * для об’єкта з ключами за id.',
+ 'item_key_path' => 'Шлях до ключа елемента',
+ 'item_key_path_hint' => 'JSON-шлях до унікального id (напр. id). Вже бачені елементи пропускаються, тож стрічка без дат все одно передає лише нові записи.',
+ 'item_date_path' => 'Шлях до дати елемента',
+ 'item_date_path_hint' => 'JSON-шлях до мітки часу елемента (напр. published_at). Переважніший за шлях ключа, коли доступний. Перше опитування фіксує базову мітку і нічого не передає, тож існуюча стрічка не заповнить систему в перший день.',
+ ],
+ ],
+
+ 'delete' => [
+ 'title' => 'Видалити автоматизацію',
+ 'description' => 'Ви впевнені, що хочете видалити цю автоматизацію? Усі запуски та елементи тригерів також буде видалено. Цю дію не можна скасувати.',
+ 'confirm' => 'Видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'flash' => [
+ 'deleted' => 'Автоматизацію успішно видалено!',
+ ],
+
+ 'errors' => [
+ 'no_active_social_accounts' => 'Для цієї автоматизації не налаштовано активних соціальних акаунтів.',
+ 'must_have_one_trigger' => 'Автоматизація має містити рівно один вузол-тригер.',
+ 'trigger_must_be_connected' => 'Вузол-тригер має бути з’єднаний хоча б з одним вузлом.',
+ 'graph_contains_cycle' => 'Граф автоматизації містить цикл.',
+ 'only_failed_can_retry' => 'Повторити можна лише запуски з помилкою.',
+ 'no_generated_post' => 'У запуску не знайдено згенерованого поста.',
+ 'webhook_server_error' => 'Помилка сервера webhook.',
+ 'webhook_request_failed' => 'Не вдалося виконати запит webhook.',
+ 'webhook_missing_url' => 'У вузлі webhook відсутній URL.',
+ 'webhook_invalid_payload_json' => 'Шаблон payload не є коректним JSON.',
+ 'url_not_allowed' => 'URL запиту вказує на приватну або недоступну адресу і було заблоковано.',
+ 'node_no_longer_exists' => 'Вузол :node_id більше не існує в автоматизації.',
+ 'no_trigger_connection' => 'До вузла-тригера не підключено жодного вузла.',
+ 'fetch_rss_missing_url' => 'У вузлі «Отримати RSS» відсутній URL стрічки.',
+ 'fetch_rss_request_failed' => 'Запит RSS-стрічки не вдався.',
+ 'fetch_rss_malformed' => 'RSS-стрічка має некоректний формат.',
+ 'http_missing_url' => 'У вузлі HTTP-запиту відсутній URL.',
+ 'http_request_exception' => 'HTTP-запит викинув виняток.',
+ 'http_request_failed' => 'HTTP-запит не вдався.',
+ 'http_items_path_not_array' => 'Шлях до елементів не повернув список.',
+ 'generate_image_format_required' => 'AI генерує лише зображення. Виберіть формат зображення (не відео).',
+ ],
+];
diff --git a/lang/uk/billing.php b/lang/uk/billing.php
new file mode 100644
index 000000000..055302ffd
--- /dev/null
+++ b/lang/uk/billing.php
@@ -0,0 +1,76 @@
+ 'Оплата',
+
+ 'past_due_notice' => [
+ 'title' => 'Прострочений платіж',
+ 'description' => 'Оновіть спосіб оплати, щоб зберегти активну підписку.',
+ 'cta' => 'Оновити оплату',
+ ],
+
+ 'annual_banner' => [
+ 'title' => '2 місяці безкоштовно',
+ 'description' => 'Перейдіть на річну оплату та платіть менше щомісяця — той самий план, нічого більше не змінюється.',
+ 'cta' => 'Перейти на річну оплату',
+ ],
+
+ 'subscribe' => [
+ 'billed_monthly' => 'Щомісячна оплата',
+ 'billed_yearly' => 'Річна оплата',
+ 'prices' => [
+ 'workspace' => ['monthly' => '$12', 'yearly_per_month' => '$10', 'yearly' => '$120'],
+ ],
+ ],
+
+ 'plan' => [
+ 'title' => 'План',
+ 'description' => 'Керуйте своїм тарифним планом.',
+ 'label' => 'План',
+ 'workspaces' => '{1}:count робочий простір|[2,*]:count робочих просторів',
+ 'per_workspace' => 'за робочий простір',
+ 'price' => 'Ціна',
+ 'month' => 'місяць',
+ 'trial' => 'Пробний період',
+ 'active' => 'Активний',
+ 'past_due' => 'Прострочено',
+ 'cancelling' => 'Скасовується',
+ 'trial_ends' => 'Пробний період закінчується',
+ ],
+
+ 'subscription' => [
+ 'title' => 'Підписка',
+ 'description' => 'Керуйте способом оплати, реквізитами та підпискою.',
+ 'payment_method' => 'Спосіб оплати',
+ 'no_payment_method' => 'Спосіб оплати ще не додано.',
+ 'expires_on' => 'Діє до :month/:year',
+ 'manage_label' => 'Підписка',
+ 'manage_stripe' => 'Керувати в Stripe',
+ ],
+
+ 'invoices' => [
+ 'title' => 'Рахунки',
+ 'description' => 'Завантажуйте минулі рахунки.',
+ 'empty' => 'Рахунків не знайдено',
+ 'paid' => 'Оплачено',
+ ],
+
+ 'flash' => [
+ 'plan_changed' => 'Ви перейшли на план :plan.',
+ 'switched_to_yearly' => 'Тепер у вас річна оплата.',
+ 'cannot_manage' => 'Лише власник облікового запису може керувати оплатою.',
+ 'credits_exhausted' => 'AI-кредити вичерпано — ваш місячний ліміт :limit використано. Оновіть план або зачекайте до наступного місяця.',
+ 'subscription_required' => 'Для використання AI-функцій потрібна активна підписка.',
+ ],
+
+ 'processing' => [
+ 'page_title' => 'Обробка...',
+ 'title' => 'Обробка вашої підписки',
+ 'description' => 'Зачекайте, поки ми налаштуємо ваш обліковий запис. Це займе лише мить.',
+ 'success_title' => 'Усе готово!',
+ 'success_description' => 'Ваша підписка активна. Перенаправляємо до ваших робочих просторів...',
+ 'cancelled_title' => 'Оформлення скасовано',
+ 'cancelled_description' => 'Оформлення підписки скасовано. Кошти не списано.',
+ 'retry' => 'Спробувати ще раз',
+ ],
+];
diff --git a/lang/uk/brands.php b/lang/uk/brands.php
new file mode 100644
index 000000000..9946a5a29
--- /dev/null
+++ b/lang/uk/brands.php
@@ -0,0 +1,39 @@
+ 'Новий бренд',
+ 'no_brands_yet' => 'Брендів ще немає',
+ 'no_brands_description' => 'Створюйте бренди, щоб організовувати соціальні акаунти за клієнтами або проєктами',
+ 'accounts_count' => ':count акаунтів',
+
+ 'create' => [
+ 'title' => 'Створити бренд',
+ 'description' => 'Дайте бренду назву для групування соціальних акаунтів',
+ 'name' => 'Назва бренду',
+ 'name_placeholder' => 'напр. Acme Corp, Особистий',
+ 'submit' => 'Створити бренд',
+ 'submitting' => 'Створення...',
+ ],
+
+ 'edit' => [
+ 'title' => 'Редагувати бренд',
+ 'description' => 'Оновіть назву цього бренду',
+ 'name' => 'Назва бренду',
+ 'name_placeholder' => 'напр. Acme Corp, Особистий',
+ 'submit' => 'Зберегти зміни',
+ 'submitting' => 'Збереження...',
+ ],
+
+ 'delete' => [
+ 'title' => 'Видалити бренд',
+ 'description' => 'Ви впевнені, що хочете видалити цей бренд? Соціальні акаунти буде від’єднано від бренду, але не видалено.',
+ 'confirm' => 'Видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'flash' => [
+ 'created' => 'Бренд успішно створено!',
+ 'updated' => 'Бренд успішно оновлено!',
+ 'deleted' => 'Бренд успішно видалено!',
+ ],
+];
diff --git a/lang/uk/calendar.php b/lang/uk/calendar.php
new file mode 100644
index 000000000..fd5ecdb45
--- /dev/null
+++ b/lang/uk/calendar.php
@@ -0,0 +1,12 @@
+ 'Календар',
+ 'today' => 'Сьогодні',
+ 'day' => 'День',
+ 'week' => 'Тиждень',
+ 'month' => 'Місяць',
+ 'new_post' => 'Новий пост',
+ 'no_content' => 'Немає контенту',
+ 'more' => '+:count ще',
+];
diff --git a/lang/uk/comments.php b/lang/uk/comments.php
new file mode 100644
index 000000000..ea942190a
--- /dev/null
+++ b/lang/uk/comments.php
@@ -0,0 +1,18 @@
+ 'Напишіть коментар...',
+ 'reply_placeholder' => 'Напишіть відповідь...',
+ 'reply' => 'Відповісти',
+ 'edit' => 'Редагувати',
+ 'delete' => 'Видалити',
+ 'edited' => 'змінено',
+ 'save' => 'Зберегти',
+ 'cancel' => 'Скасувати',
+ 'send' => 'Надіслати',
+ 'replying_to' => 'Відповідь для :name',
+ 'empty' => 'Коментарів ще немає. Почніть розмову.',
+ 'load_more' => 'Завантажити старіші коментарі',
+ 'today' => 'Сьогодні',
+ 'yesterday' => 'Вчора',
+];
diff --git a/lang/uk/common.php b/lang/uk/common.php
new file mode 100644
index 000000000..07dbb68b5
--- /dev/null
+++ b/lang/uk/common.php
@@ -0,0 +1,67 @@
+ 'Назад',
+
+ 'beta' => 'Бета',
+
+ 'confirm_modal' => [
+ 'cannot_be_undone' => 'Цю дію не можна скасувати.',
+ 'type' => 'Введіть',
+ 'to_confirm' => 'для підтвердження.',
+ 'copy_to_clipboard' => 'Копіювати в буфер обміну',
+ 'delete_keyword' => 'delete',
+ ],
+
+ 'photo_upload' => [
+ 'upload' => 'Завантажити',
+ 'uploading' => 'Завантаження...',
+ 'remove' => 'Видалити фото',
+ 'hint' => 'Рекомендовано: квадратне зображення, макс. 2 МБ.',
+ 'crop_title' => 'Обрізати зображення',
+ 'crop_description' => 'Виберіть область, яку хочете залишити.',
+ 'crop_hint' => 'Перетягніть, щоб перемістити, або за кут — щоб змінити розмір.',
+ 'crop_save' => 'Зберегти',
+ 'crop_cancel' => 'Скасувати',
+ 'crop_error' => 'Не вдалося завантажити це зображення. Спробуйте інший файл.',
+ ],
+
+ 'timezone' => [
+ 'select' => 'Виберіть часовий пояс',
+ 'search' => 'Пошук часового поясу...',
+ 'empty' => 'Часовий пояс не знайдено',
+ ],
+
+ 'date_picker' => [
+ 'select' => 'Виберіть дату',
+ ],
+
+ 'date_range_picker' => [
+ 'placeholder' => 'Виберіть діапазон дат',
+ 'today' => 'Сьогодні',
+ 'yesterday' => 'Вчора',
+ 'last_7_days' => 'Останні 7 днів',
+ 'last_30_days' => 'Останні 30 днів',
+ 'last_3_months' => 'Останні 3 місяці',
+ 'last_6_months' => 'Останні 6 місяців',
+ 'last_12_months' => 'Останні 12 місяців',
+ 'this_month' => 'Цей місяць',
+ 'last_month' => 'Минулий місяць',
+ 'year_to_date' => 'З початку року',
+ 'last_year' => 'Минулий рік',
+ ],
+
+ 'cancel' => 'Скасувати',
+ 'clear' => 'Очистити',
+ 'close' => 'Закрити',
+ 'loading_more' => 'Завантаження...',
+
+ 'actions' => [
+ 'copy' => 'Копіювати',
+ 'copied' => 'Скопійовано',
+ 'copy_failed' => 'Не вдалося скопіювати в буфер обміну',
+ ],
+];
diff --git a/lang/uk/labels.php b/lang/uk/labels.php
new file mode 100644
index 000000000..8ed2b2605
--- /dev/null
+++ b/lang/uk/labels.php
@@ -0,0 +1,54 @@
+ 'Мітки',
+ 'description' => 'Створюйте мітки для організації та категоризації постів',
+ 'search' => 'Пошук міток...',
+ 'new_label' => 'Нова мітка',
+ 'no_labels_yet' => 'Міток ще немає',
+ 'no_search_results' => 'Жодна мітка не відповідає вашому пошуку',
+ 'try_different_search' => 'Спробуйте інше ключове слово або очистіть пошук.',
+ 'create_first_label' => 'Створіть свою першу мітку',
+ 'table' => [
+ 'name' => 'Назва',
+ 'created_at' => 'Створено',
+ ],
+
+ 'actions' => [
+ 'edit' => 'Редагувати мітку',
+ 'delete' => 'Видалити мітку',
+ ],
+
+ 'create' => [
+ 'title' => 'Створити мітку',
+ 'description' => 'Дайте мітці назву та виберіть колір',
+ 'name' => 'Назва',
+ 'name_placeholder' => 'Введіть назву мітки...',
+ 'color' => 'Колір',
+ 'submit' => 'Створити мітку',
+ 'submitting' => 'Створення...',
+ ],
+
+ 'edit' => [
+ 'title' => 'Редагувати мітку',
+ 'description' => 'Оновіть назву та колір цієї мітки',
+ 'name' => 'Назва',
+ 'name_placeholder' => 'Введіть назву мітки...',
+ 'color' => 'Колір',
+ 'submit' => 'Зберегти зміни',
+ 'submitting' => 'Збереження...',
+ ],
+
+ 'delete' => [
+ 'title' => 'Видалити мітку',
+ 'description' => 'Ви впевнені, що хочете видалити цю мітку? Цю дію не можна скасувати.',
+ 'confirm' => 'Видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'flash' => [
+ 'created' => 'Мітку успішно створено!',
+ 'updated' => 'Мітку успішно оновлено!',
+ 'deleted' => 'Мітку успішно видалено!',
+ ],
+];
diff --git a/lang/uk/mail.php b/lang/uk/mail.php
new file mode 100644
index 000000000..e81e5b741
--- /dev/null
+++ b/lang/uk/mail.php
@@ -0,0 +1,22 @@
+ [
+ 'subject' => ':name згадав(ла) вас у TryPost',
+ 'title' => ':name згадав(ла) вас',
+ 'intro' => ':name згадав(ла) вас у коментарі до поста.',
+ 'cta' => 'Переглянути коментар',
+ ],
+
+ 'workspace_connections_disconnected' => [
+ 'subject' => '{1} :count акаунт потрібно перепідключити в :workspace|[2,*] :count акаунтів потрібно перепідключити в :workspace',
+ 'title' => 'Акаунти потребують перепідключення',
+ 'intro' => 'Наступні соціальні акаунти у вашому робочому просторі :workspace було від’єднано, і їх потрібно перепідключити:',
+ 'reasons_title' => 'Це могло статися через:',
+ 'reason_expired' => 'Термін дії токенів доступу минув',
+ 'reason_revoked' => 'Ви відкликали доступ TryPost на платформі',
+ 'reason_changed' => 'Платформа змінила вимоги до автентифікації',
+ 'reconnect_cta' => 'Перепідключіть ці акаунти, щоб продовжити планування та публікацію постів.',
+ 'button' => 'Перепідключити акаунти',
+ ],
+];
diff --git a/lang/uk/notifications.php b/lang/uk/notifications.php
new file mode 100644
index 000000000..0987d4518
--- /dev/null
+++ b/lang/uk/notifications.php
@@ -0,0 +1,18 @@
+ [
+ 'title' => 'Ваш пост готовий',
+ 'body' => 'AI щойно завершив роботу. Натисніть, щоб переглянути та опублікувати.',
+ ],
+ 'account_disconnected' => [
+ 'title' => 'Акаунт :platform від’єднано',
+ 'body' => ':account потрібно перепідключити',
+ ],
+ 'account_token_expired' => [
+ 'title' => 'Акаунт :platform потрібно перепідключити',
+ 'body' => 'Сесію :account завершено — перепідключіть, щоб продовжити публікацію',
+ ],
+];
diff --git a/lang/uk/onboarding.php b/lang/uk/onboarding.php
new file mode 100644
index 000000000..6c8663b1d
--- /dev/null
+++ b/lang/uk/onboarding.php
@@ -0,0 +1,57 @@
+ 'Ласкаво просимо до TryPost',
+ 'description' => 'Розкажіть, що найкраще описує вас або ваш бізнес, щоб ми могли адаптувати ваш досвід.',
+ 'continue' => 'Продовжити',
+ 'personas' => [
+ 'creator' => 'Автор контенту',
+ 'freelancer' => 'Фрілансер',
+ 'developer' => 'Розробник',
+ 'startup' => 'Стартап',
+ 'agency' => 'Агенція',
+ 'small_business' => 'Малий бізнес',
+ 'marketer' => 'Маркетолог',
+ 'online_store' => 'Інтернет-магазин',
+ 'other' => 'Інше',
+ ],
+ 'goals_title' => 'Яка ваша мета з TryPost?',
+ 'goals_description' => 'Виберіть усе, що підходить, і ми налаштуємо TryPost для вас.',
+ 'goals' => [
+ 'save_time' => 'Економити час, публікуючи всюди одразу',
+ 'ai_content' => 'Створювати пости швидше з AI',
+ 'plan_calendar' => 'Планувати пости в календарі',
+ 'stay_on_brand' => 'Тримати кожен пост у стилі бренду',
+ 'grow_audience' => 'Збільшувати аудиторію та залучення',
+ 'drive_sales' => 'Отримувати більше трафіку та продажів',
+ 'manage_clients' => 'Керувати кількома брендами або клієнтами',
+ 'team_collaboration' => 'Працювати з командою',
+ 'automate_api' => 'Автоматизувати публікацію через API, MCP або код',
+ 'track_performance' => 'Бачити, як працюють мої пости',
+ 'just_exploring' => 'Поки що просто досліджую',
+ 'other' => 'Щось інше',
+ ],
+ 'referral_source_title' => 'Як ви нас знайшли?',
+ 'referral_source_description' => 'Це допомагає нам зрозуміти, як люди дізнаються про TryPost.',
+ 'referral_source' => [
+ 'google' => 'Google або пошук',
+ 'x' => 'X (Twitter)',
+ 'linkedin' => 'LinkedIn',
+ 'youtube' => 'YouTube',
+ 'tiktok' => 'TikTok',
+ 'instagram' => 'Instagram або Threads',
+ 'reddit' => 'Reddit',
+ 'product_hunt' => 'Product Hunt',
+ 'ai_assistant' => 'AI-асистент (ChatGPT, Claude…)',
+ 'friend' => 'Друг або колега',
+ 'blog' => 'Блог, розсилка або стаття',
+ 'other' => 'Щось інше',
+ ],
+ 'connect' => [
+ 'title' => 'Підключіть першу мережу',
+ 'description' => 'Під’єднайте хоча б один соціальний акаунт, щоб почати планування. Ви зможете додати більше будь-коли.',
+ 'must_connect' => 'Підключіть хоча б одну мережу, щоб продовжити.',
+ ],
+];
diff --git a/lang/uk/pagination.php b/lang/uk/pagination.php
new file mode 100644
index 000000000..54e5a82b5
--- /dev/null
+++ b/lang/uk/pagination.php
@@ -0,0 +1,19 @@
+ '« Попередня',
+ 'next' => 'Наступна »',
+
+];
diff --git a/lang/uk/passwords.php b/lang/uk/passwords.php
new file mode 100644
index 000000000..ed9b73940
--- /dev/null
+++ b/lang/uk/passwords.php
@@ -0,0 +1,22 @@
+ 'Ваш пароль було скинуто.',
+ 'sent' => 'Ми надіслали посилання для скидання пароля на вашу електронну пошту.',
+ 'throttled' => 'Зачекайте, перш ніж повторити спробу.',
+ 'token' => 'Цей токен скидання пароля недійсний.',
+ 'user' => 'Ми не знайшли користувача з такою адресою електронної пошти.',
+
+];
diff --git a/lang/uk/posts.php b/lang/uk/posts.php
new file mode 100644
index 000000000..50a859103
--- /dev/null
+++ b/lang/uk/posts.php
@@ -0,0 +1,690 @@
+ 'Пости',
+ 'search' => 'Пошук постів...',
+ 'all_posts' => 'Усі пости',
+ 'new_post' => 'Новий пост',
+ 'no_posts' => 'Постів не знайдено',
+ 'no_search_results' => 'Жоден пост не відповідає вашому пошуку',
+ 'try_different_search' => 'Спробуйте інше ключове слово або очистіть пошук.',
+ 'start_creating' => 'Почніть із створення свого першого поста.',
+ 'filter_by_label' => 'Фільтр за міткою',
+ 'label_search_placeholder' => 'Пошук міток...',
+ 'no_labels' => 'Міток не знайдено.',
+ 'clear_label_filter' => 'Очистити фільтр міток',
+ 'table' => [
+ 'post' => 'Пост',
+ 'status' => 'Статус',
+ 'content' => 'Вміст',
+ 'platforms' => 'Платформи',
+ 'labels' => 'Мітки',
+ 'scheduled_at' => 'Дата',
+ 'actions' => '',
+ ],
+ 'manage_posts' => 'Керуйте всіма своїми постами',
+ 'delete_confirm' => 'Ви впевнені, що хочете видалити цей пост?',
+ 'by' => 'від',
+
+ 'actions' => [
+ 'view' => 'Переглянути пост',
+ 'delete' => 'Видалити',
+ 'duplicate' => 'Дублювати',
+ 'copy_id' => 'Копіювати ID',
+ 'copied' => 'ID скопійовано в буфер обміну',
+ ],
+
+ 'form' => [
+ 'post_type' => 'Тип поста',
+ 'board' => 'дошка',
+ 'select_board' => 'Виберіть дошку',
+ 'search_board' => 'Пошук дошки...',
+ 'no_board_found' => 'дошку не знайдено',
+ 'media' => 'Медіа',
+ 'min' => 'Мін.',
+ 'uploading' => 'Завантаження...',
+ 'drop_to_upload' => 'Відпустіть, щоб завантажити',
+ 'drag_and_drop' => 'Перетягніть або натисніть, щоб завантажити',
+ 'photos_and_videos' => 'Фото та відео',
+ 'photos_only' => 'Лише фото',
+ 'videos_only' => 'Лише відео',
+ 'drag_to_reorder' => 'Перетягніть, щоб змінити порядок',
+ 'caption' => 'Підпис',
+ 'write_caption' => 'Напишіть підпис...',
+ 'content_exceeds_platform' => ':platform: перевищення на :over симв. (макс. :limit).',
+ 'tiktok' => [
+ 'settings' => 'Налаштування TikTok',
+ 'variant_label' => 'Тип поста',
+ 'variant' => [
+ 'video' => 'Відео',
+ 'photo' => 'Фотокарусель',
+ ],
+ 'posting_to' => 'Публікація в',
+ 'privacy_level' => 'Хто може бачити це відео?',
+ 'privacy_placeholder' => 'Виберіть, хто може бачити цей пост',
+ 'privacy' => [
+ 'public' => 'Доступно всім',
+ 'friends' => 'Взаємні друзі-підписники',
+ 'followers' => 'Підписники',
+ 'private' => 'Лише я',
+ 'private_disabled_branded' => 'Видимість брендованого контенту не можна встановити як «Лише я».',
+ ],
+ 'privacy_hint' => 'Доступні варіанти залежать від налаштувань вашого акаунта TikTok.',
+ 'auto_add_music' => 'Автоматично додати музику',
+ 'auto_add_music_hint' => 'Ця функція доступна лише для фото. Буде додано музику за замовчуванням, яку можна змінити пізніше.',
+ 'yes' => 'Так',
+ 'no' => 'Ні',
+ 'allow_users' => 'Дозволити користувачам:',
+ 'comments' => 'Коментувати',
+ 'duet' => 'Duet',
+ 'stitch' => 'Stitch',
+ 'is_aigc' => 'Відео створено за допомогою AI',
+ 'disclose' => 'Розкрити вміст відео',
+ 'disclose_hint' => 'Увімкніть, щоб указати, що це відео просуває товари чи послуги в обмін на щось цінне. Ваше відео може просувати вас, третю сторону або обох.',
+ 'promotional_organic_title' => 'Ваше фото/відео буде позначено як «Рекламний контент».',
+ 'promotional_paid_title' => 'Ваше фото/відео буде позначено як «Платне партнерство».',
+ 'promotional_description' => 'Це не можна змінити після публікації відео.',
+ 'compliance_incomplete' => 'Потрібно вказати, чи ваш контент просуває вас, третю сторону чи обох.',
+ 'privacy_required' => 'Під час публікації потрібно вказати рівень приватності TikTok.',
+ 'branded_cleared_private' => 'Налаштування приватності скинуто, оскільки брендований контент не може бути приватним.',
+ 'interaction_disabled_by_creator' => 'Вимкнено в налаштуваннях вашого акаунта TikTok',
+ 'max_duration_exceeded' => 'Відео триває :duration с, але цей акаунт може публікувати відео тривалістю не більше :max с.',
+ 'processing_hint' => 'Після публікації може знадобитися кілька хвилин, щоб контент обробився та з’явився у вашому профілі TikTok.',
+ 'brand_organic' => 'Ваш бренд',
+ 'brand_organic_hint' => 'Ви просуваєте себе або власний бренд. Це відео буде класифіковано як Brand Organic.',
+ 'brand_content' => 'Брендований контент',
+ 'brand_content_hint' => 'Ви просуваєте інший бренд або третю сторону. Це відео буде класифіковано як Branded Content.',
+ 'compliance' => [
+ 'agree' => 'Публікуючи, ви погоджуєтеся з',
+ 'music_usage' => 'Підтвердженням використання музики TikTok',
+ 'and' => 'та',
+ 'branded_policy' => 'Політикою брендованого контенту',
+ ],
+ ],
+ 'instagram' => [
+ 'settings' => 'Налаштування Instagram',
+ 'posting_to' => 'Публікація в',
+ 'variant_label' => 'Тип поста',
+ 'variant' => [
+ 'feed' => 'Пост у стрічці',
+ 'reel' => 'Reels',
+ 'story' => 'Stories',
+ ],
+ 'aspect_label' => 'Співвідношення сторін',
+ 'aspect' => [
+ 'square' => 'Квадрат (1:1)',
+ 'portrait' => 'Портрет (4:5)',
+ 'landscape' => 'Альбомна (16:9)',
+ 'original' => 'Оригінал',
+ ],
+ ],
+ 'facebook' => [
+ 'settings' => 'Налаштування Facebook',
+ 'posting_to' => 'Публікація в',
+ 'variant_label' => 'Тип поста',
+ 'variant' => [
+ 'post' => 'Пост',
+ 'reel' => 'Reels',
+ 'story' => 'Stories',
+ ],
+ 'aspect_label' => 'Співвідношення сторін',
+ 'aspect' => [
+ 'square' => 'Квадрат (1:1)',
+ 'portrait' => 'Портрет (4:5)',
+ 'landscape' => 'Альбомна (16:9)',
+ 'original' => 'Оригінал',
+ ],
+ ],
+ 'linkedin' => [
+ 'settings' => 'Налаштування LinkedIn',
+ 'settings_page' => 'Налаштування сторінки LinkedIn',
+ 'posting_to' => 'Публікація в',
+ 'document_title' => 'Назва документа',
+ 'document_title_placeholder' => 'Відображається у вашому пості з PDF-документом',
+ ],
+ 'pinterest' => [
+ 'settings' => 'Налаштування Pinterest',
+ 'posting_to' => 'Публікація в',
+ 'variant_label' => 'Тип піна',
+ 'variant' => [
+ 'pin' => 'Pin',
+ 'video_pin' => 'Відео-пін',
+ 'carousel' => 'Карусель',
+ ],
+ 'board' => 'дошка',
+ 'select_board' => 'Виберіть дошку',
+ 'no_boards' => 'дошок Pinterest не знайдено. Спочатку створіть дошку у своєму акаунті Pinterest.',
+ 'boards_truncated' => 'Деякі дошки не вдалося завантажити. Якщо вашої немає, відкрийте Pinterest і спробуйте знову.',
+ 'search_board' => 'Пошук досок...',
+ 'no_board_found' => 'Жодна дошка не відповідає вашому пошуку.',
+ 'board_required' => 'Виберіть дошку Pinterest, щоб опублікувати цей пост.',
+ 'title' => 'Заголовок',
+ 'title_placeholder' => 'Необовʼязковий заголовок піна',
+ 'link' => 'Посилання призначення',
+ 'link_placeholder' => 'https://example.com',
+ 'link_invalid' => 'Вкажіть коректне посилання призначення (http або https).',
+ 'title_max' => 'Заголовок не може бути довшим за 100 символів.',
+ 'link_max' => 'Посилання призначення не може бути довшим за 2048 символів.',
+ ],
+ 'discord' => [
+ 'settings' => 'Налаштування Discord',
+ 'posting_to' => 'Публікація в',
+ 'channel' => 'Канал',
+ 'select_channel' => 'Виберіть канал',
+ 'loading_channels' => 'Завантаження каналів…',
+ 'search_channel' => 'Пошук каналів…',
+ 'no_channels' => 'Каналів не знайдено.',
+ 'channel_required' => 'Виберіть канал Discord, щоб опублікувати цей пост.',
+ 'mentions' => 'Згадки',
+ 'search_mention' => 'Згадайте роль або учасника…',
+ 'embeds' => 'Вбудовування',
+ 'embed' => 'Вбудовування',
+ 'add_embed' => 'Додати вбудовування',
+ 'embed_title' => 'Заголовок вбудовування',
+ 'embed_description' => 'Опис вбудовування',
+ 'embed_url' => 'URL вбудовування',
+ 'embed_image' => 'URL зображення',
+ 'embed_color' => 'Колір',
+ ],
+ 'warnings' => [
+ 'no_variant' => 'Виберіть тип поста, щоб продовжити.',
+ 'requires_media' => 'Цей тип поста потребує принаймні одного зображення або відео.',
+ 'max_files_exceeded' => 'Цей тип поста приймає не більше :max медіафайлів (у вас :current).',
+ 'min_files_required' => 'Цей тип поста потребує принаймні :min медіафайлів (у вас :current).',
+ 'no_video_allowed' => 'Цей тип поста не приймає відео.',
+ 'no_image_allowed' => 'Цей тип поста приймає лише відео.',
+ 'no_document_allowed' => 'Цей тип поста не приймає PDF-документи.',
+ 'no_mixed_media' => 'Зображення та відео не можна поєднувати в одному пості.',
+ 'document_not_alone' => 'PDF потрібно публікувати окремо, без інших зображень або відео.',
+ 'gif_not_allowed' => 'Ця платформа не приймає GIF. Видаліть GIF або виберіть іншу мережу.',
+ 'image_too_large' => 'Зображення перевищує ліміт :max для цього типу поста (у вас :current).',
+ 'video_too_large' => 'Відео перевищує ліміт :max для цього типу поста (у вас :current).',
+ 'document_too_large' => 'PDF перевищує ліміт :max для цього типу поста (у вас :current).',
+ 'video_too_long' => 'Відео триває :current, але цей тип поста дозволяє не більше :max.',
+ 'aspect_ratio_too_narrow' => 'Співвідношення сторін :current занадто вузьке для цього типу поста (мін. :min).',
+ 'aspect_ratio_too_wide' => 'Співвідношення сторін :current занадто широке для цього типу поста (макс. :max).',
+ ],
+ ],
+
+ 'status' => [
+ 'pending' => 'Очікує',
+ 'draft' => 'Чернетка',
+ 'scheduled' => 'Заплановано',
+ 'publishing' => 'Публікується',
+ 'retrying' => 'Повторна спроба',
+ 'published' => 'Опубліковано',
+ 'partially_published' => 'Частково опубліковано',
+ 'failed' => 'Помилка',
+ ],
+
+ 'descriptions' => [
+ 'draft' => 'Пости, що очікують на планування',
+ 'scheduled' => 'Пости, заплановані до публікації',
+ 'published' => 'Уже опубліковані пости',
+ ],
+
+ 'ai' => [
+ 'generate' => [
+ 'button_tooltip' => 'Згенерувати за допомогою AI',
+ 'title' => 'Згенерувати пост за допомогою AI',
+ 'description' => 'Опишіть, про що має бути пост. AI використає контекст вашого бренду, щоб написати його.',
+ 'prompt_label' => 'Про що цей пост?',
+ 'prompt_placeholder' => 'наприклад, Оголосити нашу нову функцію генерації зображень для каруселей',
+ 'preview_label' => 'Попередній перегляд',
+ 'start' => 'Згенерувати',
+ 'apply' => 'Використати цей вміст',
+ 'retry' => 'Спробувати знову',
+ 'cancel' => 'Скасувати',
+ ],
+ 'review' => [
+ 'button_tooltip' => 'Перевірити за допомогою AI',
+ 'title' => 'Перевірити пост за допомогою AI',
+ 'description' => 'AI перевіряє граматику, орфографію та зрозумілість. Застосовуйте пропозиції, з якими ви погоджуєтеся.',
+ 'loading' => 'Перевіряємо ваш текст...',
+ 'no_issues' => 'Проблем не знайдено. Виглядає добре.',
+ 'original' => 'Оригінал',
+ 'suggestion' => 'Пропозиція',
+ 'apply' => 'Застосувати',
+ 'apply_all' => 'Застосувати все',
+ 'applied' => 'Застосовано',
+ 'cancel' => 'Скасувати',
+ ],
+ 'image_regenerate' => [
+ 'button' => 'Скоригувати',
+ 'title' => 'Скоригувати AI-зображення',
+ 'description' => 'Опишіть правку. Нове зображення замінить поточне та збереже своє місце в каруселі.',
+ 'instruction_label' => 'Інструкція',
+ 'instruction_placeholder' => 'наприклад, Виправити друкарську помилку в заголовку та зробити фон світлішим.',
+ 'processing' => 'Перегенеровуємо зображення... це може зайняти кілька секунд.',
+ 'submit' => 'Перегенерувати зображення',
+ 'cancel' => 'Скасувати',
+ 'success' => 'Зображення оновлено. Нова версія замінила попередню у вашому пості.',
+ 'fallback_title' => 'Покращити текст на цьому зображенні',
+ 'errors' => [
+ 'required' => 'Інструкція обов’язкова.',
+ 'start_failed' => 'Не вдалося розпочати перегенерацію.',
+ 'unavailable' => 'Зараз неможливо перегенерувати це зображення.',
+ 'timeout' => 'Перегенерація триває довше, ніж очікувалося. Спробуйте через мить.',
+ 'media_not_found' => 'Медіафайл не знайдено.',
+ 'not_ai_media' => 'Перегенеровувати можна лише медіа, згенеровані AI.',
+ ],
+ ],
+ 'templates' => [
+ 'image_card' => [
+ 'name' => 'Пост із зображенням',
+ 'description' => 'AI-зображення із заголовком та підписом.',
+ ],
+ 'carousel' => [
+ 'name' => 'Карусель',
+ 'description' => 'Карусель із кількох слайдів із підписом.',
+ ],
+ 'tweet_card' => [
+ 'name' => 'Картка твіта',
+ 'description' => 'Ваш пост у стилі публікації X/Twitter.',
+ ],
+ 'tweet_card_image' => [
+ 'name' => 'Картка твіта з фото',
+ 'description' => 'Ваш пост у вигляді картки X/Twitter на розмитому фото.',
+ ],
+ ],
+ ],
+
+ 'show' => [
+ 'title' => 'Деталі поста',
+ 'edit' => 'Редагувати',
+ 'back' => 'Назад',
+ 'no_content' => 'Без підпису',
+ 'platforms' => 'Платформи',
+ 'no_platforms' => 'Платформи не вибрано.',
+ 'view_on_platform' => 'Переглянути на платформі',
+ 'published_on' => 'Опубліковано :date',
+ 'scheduled_for' => 'Заплановано на :date',
+ 'draft' => 'Чернетка',
+ 'status_pending' => 'Очікує',
+ 'metrics' => 'Метрики',
+ 'metrics_loading' => 'Завантаження метрик…',
+ 'metrics_unavailable' => 'Метрики для цієї платформи поки недоступні.',
+ 'metrics_empty' => 'Метрики відсутні.',
+ ],
+
+ 'edit' => [
+ 'title' => 'Редагувати пост',
+ 'view_title' => 'Переглянути пост',
+ 'labels' => 'Мітки',
+ 'no_labels' => 'Мітки ще не створено',
+ 'schedule' => 'Запланувати',
+ 'pick_time' => 'Виберіть час',
+ 'pick_time_past' => 'Виберіть дату та час у майбутньому.',
+ 'post_now' => 'Опублікувати зараз',
+ 'time' => 'Час',
+ 'cancel' => 'Скасувати',
+ 'delete' => 'Видалити',
+ 'schedule_for' => 'Запланувати на',
+ 'schedule_date' => 'Дата планування',
+ 'unschedule' => 'Скасувати планування',
+ 'saving' => 'Збереження...',
+ 'saved' => 'Збережено',
+ 'draft' => 'Чернетка',
+ 'media' => 'Медіа',
+ 'add_media' => 'Додати медіа',
+ 'caption' => 'Підпис',
+ 'caption_placeholder' => 'Напишіть підпис...',
+ 'compose_title' => 'Створити пост',
+ 'compose_subtitle' => 'Складіть повідомлення та додайте медіа',
+ 'preview_empty' => [
+ 'title' => 'Платформу не вибрано',
+ 'description' => 'Виберіть платформу для публікації, щоб побачити попередній перегляд.',
+ ],
+ 'drop_zone_title' => 'Додати медіа',
+ 'drop_zone_subtitle' => 'Перетягніть файли або натисніть, щоб обрати',
+ 'add' => 'Додати',
+ 'publish_to' => 'Опублікувати в',
+ 'organize' => 'Організувати',
+ 'signatures' => 'Підписи',
+ 'view_on_platform' => 'Переглянути на платформі',
+ 'platform_status' => 'Статус платформи',
+ 'compliance_incomplete' => 'Деякі налаштування платформи не заповнені або несумісні з прикріпленим медіа.',
+ 'compliance' => [
+ 'requires_content_or_media' => 'Додайте текст або медіа для публікації.',
+ 'requires_media' => 'Додайте зображення або відео для публікації тут.',
+ 'too_many_files' => 'Для цього формату допускається не більше :max файлів.',
+ 'too_few_files' => 'Додайте принаймні :min файлів для цього формату.',
+ 'no_videos' => 'Для цього формату допустимі лише зображення.',
+ 'no_images' => 'Для цього формату допустимі лише відео.',
+ 'no_mixed_media' => 'Зображення та відео не можна поєднувати в одному пості.',
+ 'no_gifs' => 'GIF тут не підтримуються.',
+ 'no_documents' => 'PDF-документи не підтримуються цим форматом.',
+ 'document_not_alone' => 'PDF потрібно публікувати окремо, без інших зображень або відео.',
+ 'video_too_large' => 'Відео перевищує ліміт розміру для цієї платформи.',
+ 'video_too_long' => 'Для цього формату відео має бути коротшим за :seconds секунд.',
+ 'image_too_large' => 'Зображення перевищує ліміт розміру для цієї платформи.',
+ 'document_too_large' => 'PDF перевищує ліміт розміру для цієї платформи.',
+ 'aspect_ratio_invalid' => 'Співвідношення сторін не підтримується цим форматом.',
+ 'no_content_type' => 'Виберіть тип контенту для цієї платформи.',
+ 'requires_text' => 'Додайте текст — для цього формату потрібен заголовок.',
+ ],
+ 'publishing' => 'Публікується...',
+ 'publishing_overlay_title' => 'Ваш пост публікується',
+ 'publishing_overlay_subtitle' => 'Це може зайняти кілька миттєвостей. Ви можете спокійно покинути цю сторінку.',
+ 'scheduled_overlay_title' => 'Цей пост заплановано',
+ 'scheduled_overlay_subtitle' => 'Заплановано на :date. Спочатку скасуйте планування, щоб внести зміни.',
+ 'unschedule_cta' => 'Скасувати планування для редагування',
+
+ 'tabs' => [
+ 'compose' => 'Написати',
+ 'preview' => 'Попередній перегляд',
+ 'channels' => 'Канали',
+ 'comments' => 'Коментарі',
+ 'comments_empty' => 'Поки що немає коментарів.',
+ ],
+
+ 'media_picker' => [
+ 'title' => 'Вибрати з галереї',
+ 'search' => 'Пошук медіа...',
+ 'empty' => 'У галереї поки немає медіа',
+ 'cancel' => 'Скасувати',
+ 'add' => 'Додати',
+ 'add_count' => 'Додати :count',
+ ],
+
+ 'alt_text' => [
+ 'label' => 'Альтернативний текст',
+ 'edit' => 'Редагувати альтернативний текст',
+ 'placeholder' => 'Опишіть це зображення для людей, які використовують програми зчитування з екрана',
+ 'hint' => 'Публікується на платформах, що підтримують описи зображень.',
+ 'save' => 'Зберегти',
+ ],
+
+ 'emoji_picker' => [
+ 'search' => 'Пошук емодзі',
+ 'empty' => 'Емодзі не знайдено',
+ 'recent' => 'Часто використовувані',
+ 'smileys' => 'Смайлики та емоції',
+ 'people' => 'Люди та тіло',
+ 'nature' => 'Тварини та природа',
+ 'food' => 'Їжа та напої',
+ 'activities' => 'Активності',
+ 'travel' => 'Подорожі та місця',
+ 'objects' => 'Об’єкти',
+ 'symbols' => 'Символи',
+ 'flags' => 'Прапори',
+ ],
+
+ 'status' => [
+ 'pending' => 'Очікує',
+ 'scheduled' => 'Заплановано',
+ 'published' => 'Опубліковано',
+ 'publishing' => 'Публікується...',
+ 'retrying' => 'Повторна спроба...',
+ 'failed' => 'Помилка',
+ ],
+
+ 'delete_modal' => [
+ 'title' => 'Видалити пост',
+ 'description' => 'Ви впевнені, що хочете видалити цей пост? Цю дію не можна скасувати.',
+ 'action' => 'Видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'sync_enable' => [
+ 'title' => 'Увімкнути синхронізацію?',
+ 'description' => 'Усі платформи використовуватимуть однаковий вміст. Будь-які індивідуальні правки для окремих платформ буде замінено поточним вмістом.',
+ 'cancel' => 'Скасувати',
+ 'action' => 'Увімкнути синхронізацію',
+ ],
+
+ 'sync_disable' => [
+ 'title' => 'Вимкнути синхронізацію?',
+ 'description' => 'Кожна платформа збереже свій поточний вміст, але майбутні правки застосовуватимуться лише до платформи, яку ви редагуєте.',
+ 'customize_note' => 'Ви зможете налаштовувати вміст для кожної платформи окремо.',
+ 'cancel' => 'Скасувати',
+ 'action' => 'Вимкнути синхронізацію',
+ ],
+
+ 'platforms_dialog' => [
+ 'title' => 'Виберіть платформи',
+ 'description' => 'Виберіть, на які платформи опублікувати цей пост.',
+ ],
+
+ 'signatures_modal' => [
+ 'search' => 'Пошук підписів...',
+ 'no_results' => 'Підписів не знайдено.',
+ ],
+
+ 'validation' => [
+ 'select_board' => 'Виберіть дошку',
+ 'images_not_supported' => 'Зображення не підтримуються',
+ 'videos_not_supported' => 'Відео не підтримуються',
+ 'max_images' => 'Не більше :count зображень',
+ 'requires_media' => 'Потрібне медіа',
+ 'requires_content' => 'Потрібен текстовий вміст',
+ 'exceeded' => 'перевищено на :count',
+ 'does_not_support_images' => ':platform не підтримує зображення',
+ 'supports_up_to_images' => ':platform підтримує до :count зображень',
+ 'does_not_support_videos' => ':platform не підтримує відео',
+ ],
+ ],
+
+ 'content_types' => [
+ 'instagram_feed' => [
+ 'label' => 'Пост у стрічці',
+ 'description' => 'З’являється у вашій стрічці та профілі',
+ ],
+ 'instagram_reel' => [
+ 'label' => 'Reels',
+ 'description' => 'Коротке відео до 15 хвилин',
+ ],
+ 'instagram_story' => [
+ 'label' => 'Stories',
+ 'description' => 'Зникає через 24 години',
+ ],
+ 'linkedin_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Звичайний пост — одне зображення, кілька зображень, відео або PDF',
+ ],
+ 'linkedin_page_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Звичайний пост — одне зображення, кілька зображень, відео або PDF',
+ ],
+ 'facebook_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Звичайний пост на вашій сторінці',
+ ],
+ 'facebook_reel' => [
+ 'label' => 'Reels',
+ 'description' => 'Коротке відео до 90 секунд',
+ ],
+ 'facebook_story' => [
+ 'label' => 'Stories',
+ 'description' => 'Вертикальна відеоісторія, до 60 секунд',
+ ],
+ 'tiktok_video' => [
+ 'label' => 'Відео',
+ 'description' => 'Коротке відео',
+ ],
+ 'tiktok_photo' => [
+ 'label' => 'Фотокарусель',
+ 'description' => 'До 35 фото у вигляді каруселі',
+ ],
+ 'youtube_short' => [
+ 'label' => 'Short',
+ 'description' => 'Вертикальне відео до 3 хвилин',
+ ],
+ 'x_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Твіт із текстом та медіа',
+ ],
+ 'threads_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Текстовий пост із необов’язковим медіа',
+ ],
+ 'pinterest_pin' => [
+ 'label' => 'Pin',
+ 'description' => 'Звичайний пін із зображенням',
+ ],
+ 'pinterest_video_pin' => [
+ 'label' => 'Відео-пін',
+ 'description' => 'Відео-пін (4 с – 15 хв)',
+ ],
+ 'pinterest_carousel' => [
+ 'label' => 'Карусель',
+ 'description' => 'Карусель із кількох зображень (2–5 зображень)',
+ ],
+ 'bluesky_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Текстовий пост із необов’язковими зображеннями',
+ ],
+ 'mastodon_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Текстовий пост із необов’язковим медіа',
+ ],
+ 'telegram_post' => [
+ 'label' => 'Пост',
+ 'description' => 'Текстовий пост із необов’язковим медіа',
+ ],
+ 'discord_message' => [
+ 'label' => 'Повідомлення',
+ 'description' => 'Повідомлення в канал Discord із необов’язковим медіа та вбудовуваннями',
+ ],
+ ],
+
+ 'platforms' => [
+ 'linkedin' => 'LinkedIn',
+ 'linkedin-page' => 'Сторінка LinkedIn',
+ 'x' => 'X',
+ 'tiktok' => 'TikTok',
+ 'youtube' => 'YouTube Shorts',
+ 'facebook' => 'Сторінка Facebook',
+ 'instagram' => 'Instagram',
+ 'threads' => 'Threads',
+ 'pinterest' => 'Pinterest',
+ 'bluesky' => 'Bluesky',
+ 'mastodon' => 'Mastodon',
+ ],
+
+ 'flash' => [
+ 'scheduled' => 'Пост успішно заплановано!',
+ 'deleted' => 'Пост успішно видалено!',
+ 'duplicated' => 'Пост продубльовано як чернетку.',
+ 'cannot_edit_finalized' => 'Цей пост уже оброблено і не можна опублікувати повторно. Продублюйте його, щоб спробувати знову.',
+ 'cannot_delete_published' => 'Опубліковані пости не можна видалити.',
+ 'connect_first' => 'Підключіть принаймні одну соціальну мережу перед створенням поста.',
+ ],
+
+ 'errors' => [
+ 'account_disconnected' => 'Соціальний акаунт відключено',
+ 'account_inactive' => 'Соціальний акаунт деактивовано',
+ 'account_token_expired' => 'Сесія соціального акаунта закінчилася — перепідключіть',
+ ],
+
+ 'delete' => [
+ 'title' => 'Видалити пост?',
+ 'description' => 'Цю дію не можна скасувати. Пост і все його медіа буде безповоротно видалено.',
+ 'confirm' => 'Так, видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'create' => [
+ 'title' => 'Створити новий пост',
+ 'description' => 'Виберіть, з чого хочете почати.',
+ 'scratch_title' => 'Почати з нуля',
+ 'scratch_description' => 'Відкрийте порожній пост і напишіть усе самі.',
+ 'ai_title' => 'Згенерувати за допомогою AI',
+ 'ai_description' => 'Опишіть, що хочете, і AI згенерує вміст за вас.',
+ 'ai_configure_description' => 'Виберіть формат і опишіть пост, який хочете створити.',
+ 'ai_pick_template_description' => 'Виберіть стиль для вашого AI-поста.',
+ 'template_title' => 'Використати шаблон',
+ 'template_description' => 'Виберіть із наших готових шаблонів і налаштуйте.',
+ 'coming_soon' => 'Незабаром',
+
+ 'preview' => [
+ 'image_title' => 'Заголовок зображення',
+ 'image_body' => 'Текст зображення',
+ ],
+
+ 'steps' => [
+ 'template_picker_title' => 'Виберіть стиль',
+ 'format_title' => 'Виберіть формат',
+ 'format_description' => 'Виберіть тип поста, який хочете створити.',
+ 'account_title' => 'Виберіть акаунт',
+ 'account_description' => 'Виберіть соціальний акаунт для публікації.',
+ 'media_title' => 'Параметри медіа',
+ 'media_carousel' => 'Скільки слайдів?',
+ 'media_optional' => 'Додати зображення?',
+ 'media_optional_label' => 'Скільки зображень?',
+ 'media_none' => 'Немає',
+ 'media_count_label' => 'Кількість зображень',
+ 'brand_colors_label' => 'Кольори бренду',
+ 'brand_colors_description' => 'Застосовуйте фірмову палітру вашого workspace до згенерованих зображень. Вимкніть, щоб AI підібрав кольори під вміст.',
+ 'prompt_title' => 'Опишіть свій пост',
+ 'prompt_label' => 'Про що цей пост?',
+ 'prompt_placeholder' => 'наприклад, Оголосити нашу нову функцію каруселі для Instagram',
+ 'preview_error' => 'Щось пішло не так. Спробуйте ще раз.',
+ 'loading_page_title' => 'Генеруємо ваш пост',
+ 'loading_eta' => 'Орієнтовний час: близько :minutes.',
+ 'loading_eta_minute_one' => '1 хвилина',
+ 'loading_eta_minute_other' => ':count хвилин',
+ 'loading_leave_title' => 'Ви можете продовжувати працювати.',
+ 'loading_leave_body' => 'Ми повідомимо вас, коли пост буде готовий.',
+ 'loading_leave_cta' => 'Перейти до календаря',
+ 'loading_create_another_cta' => 'Створити ще один пост',
+ 'loading_tip_credits' => 'Кожне AI-зображення використовує близько 15 кредитів.',
+ 'loading_tip_edit' => 'Ви зможете відредагувати все, коли пост буде готовий.',
+ 'loading_tip_draft' => 'Згенеровані пости потрапляють у чернетки.',
+ 'loading_tip_brand' => 'Налаштуйте параметри бренду, щоб впливати на майбутні пости.',
+ 'loading_tip_carousel' => 'У каруселі створюється один слайд на кожне завантажене зображення.',
+ 'loading_tip_quality' => 'Якість зображень налаштована на баланс швидкості та вартості.',
+ 'create' => 'Створити пост',
+ 'back' => 'Назад',
+ 'next' => 'Продовжити',
+ 'cancel' => 'Скасувати',
+ 'discard' => 'Скасувати',
+ 'retry' => 'Спробувати знову',
+ 'no_platforms' => 'Немає підключених акаунтів',
+ 'connect_first' => 'Підключіть принаймні один соціальний акаунт, щоб використовувати генерацію AI.',
+ 'no_account_for_template' => 'Підключіть сумісний акаунт, щоб використати цей шаблон.',
+
+ 'format' => [
+ 'instagram_feed' => 'Пост у стрічці Instagram',
+ 'instagram_carousel' => 'Карусель Instagram',
+ 'linkedin_post' => 'Пост LinkedIn',
+ 'linkedin_page_post' => 'Пост сторінки LinkedIn',
+ 'x_post' => 'Пост X',
+ 'bluesky_post' => 'Пост Bluesky',
+ 'threads_post' => 'Пост Threads',
+ 'mastodon_post' => 'Пост Mastodon',
+ 'telegram_post' => 'Пост Telegram',
+ 'discord_message' => 'Повідомлення Discord',
+ 'facebook_post' => 'Пост Facebook',
+ 'pinterest_pin' => 'Pin Pinterest',
+ 'instagram_story' => 'Stories Instagram',
+ 'facebook_story' => 'Stories Facebook',
+ ],
+ ],
+ ],
+
+ 'templates' => [
+ 'browser_title' => 'Виберіть шаблон',
+ 'browser_description' => 'Почніть із готового шаблону та адаптуйте його.',
+ 'search_placeholder' => 'Пошук шаблонів…',
+ 'no_search_results' => 'Жоден шаблон не відповідає вашому пошуку',
+ 'try_different_search' => 'Спробуйте інше ключове слово або очистіть пошук.',
+ 'slides_count' => '{count} слайд|{count} слайди|{count} слайдів',
+ 'all_platforms' => 'Усі платформи',
+ 'platform_search_placeholder' => 'Пошук платформи…',
+ 'no_platform_match' => 'Жодна платформа не відповідає.',
+ 'use_this' => 'Використати цей шаблон',
+ 'no_templates' => 'Немає доступних шаблонів.',
+ 'applying' => 'Застосовуємо шаблон…',
+ 'category' => [
+ 'product_launch' => 'Запуск продукту',
+ 'promotion' => 'Акція',
+ 'educational' => 'Навчальне',
+ 'behind_the_scenes' => 'За кулісами',
+ 'testimonial' => 'Відгук',
+ 'industry_tip' => 'Галузева порада',
+ 'event' => 'Подія',
+ 'engagement' => 'Залучення',
+ ],
+ ],
+];
diff --git a/lang/uk/settings.php b/lang/uk/settings.php
new file mode 100644
index 000000000..c191c37da
--- /dev/null
+++ b/lang/uk/settings.php
@@ -0,0 +1,384 @@
+ 'Налаштування',
+ 'description' => 'Керуйте профілем та налаштуваннями облікового запису',
+
+ 'hub' => [
+ 'title' => 'Налаштування',
+ 'description' => 'Виберіть, чим хочете керувати.',
+ 'profile' => [
+ 'title' => 'Профіль',
+ 'description' => 'Оновіть особисті дані, пароль і налаштування сповіщень.',
+ ],
+ 'workspace' => [
+ 'title' => 'Робочий простір',
+ 'description' => 'Налаштуйте робочий простір, бренд, учасників і API-ключі.',
+ ],
+ 'account' => [
+ 'title' => 'Обліковий запис',
+ 'description' => 'Керуйте даними облікового запису, використанням і оплатою.',
+ ],
+ ],
+
+ 'nav' => [
+ 'profile' => 'Профіль',
+ 'authentication' => 'Автентифікація',
+ 'workspace' => 'Робочий простір',
+ 'members' => 'Учасники',
+ 'notifications' => 'Сповіщення',
+ 'billing' => 'Оплата',
+ ],
+
+ 'notifications' => [
+ 'title' => 'Налаштування сповіщень',
+ 'heading' => 'Email-сповіщення',
+ 'description' => 'Виберіть, які email-сповіщення ви хочете отримувати',
+ 'post_published' => 'Пост опубліковано',
+ 'post_published_description' => 'Отримуйте email, коли ваш пост успішно опубліковано',
+ 'post_failed' => 'Помилка публікації',
+ 'post_failed_description' => 'Отримуйте email, коли публікація поста не вдалася',
+ 'account_disconnected' => 'Акаунт від’єднано',
+ 'account_disconnected_description' => 'Отримуйте email, коли соціальний акаунт від’єднано',
+ 'save' => 'Зберегти налаштування',
+ ],
+
+ 'profile' => [
+ 'title' => 'Налаштування профілю',
+ 'photo_heading' => 'Фото профілю',
+ 'photo_description' => 'Завантажте фото профілю',
+ 'heading' => 'Інформація профілю',
+ 'description' => 'Оновіть ім’я та адресу email',
+ 'avatar' => 'Аватар',
+ 'name' => 'Ім’я',
+ 'name_placeholder' => 'Повне ім’я',
+ 'email' => 'Адреса email',
+ 'email_placeholder' => 'Адреса email',
+ 'email_unverified' => 'Вашу адресу email не підтверджено.',
+ 'resend_verification' => 'Натисніть тут, щоб надіслати лист підтвердження ще раз.',
+ 'verification_sent' => 'Нове посилання для підтвердження надіслано на вашу адресу email.',
+ 'save' => 'Зберегти',
+ ],
+
+ 'authentication' => [
+ 'title' => 'Автентифікація',
+ 'page_title' => 'Налаштування автентифікації',
+ 'sessions' => [
+ 'title' => 'Активні сесії',
+ 'description' => 'Якщо помітили щось підозріле, вийдіть з інших пристроїв.',
+ 'unknown_browser' => 'Невідомий браузер',
+ 'unknown_ip' => 'Невідомий IP',
+ 'on' => 'на',
+ 'active_now' => 'Активна зараз',
+ 'log_out_others' => 'Вийти з інших пристроїв',
+ 'modal_title' => 'Вийти з інших пристроїв',
+ 'modal_description_password' => 'Введіть поточний пароль, щоб підтвердити вихід з інших сесій браузера.',
+ 'modal_description_email' => 'Введіть адресу email, щоб підтвердити вихід з інших сесій браузера.',
+ 'password_placeholder' => 'Поточний пароль',
+ 'email_placeholder' => 'Email вашого облікового запису',
+ 'cancel' => 'Скасувати',
+ 'submit' => 'Вийти з інших пристроїв',
+ 'email_mismatch' => 'Адреса email не збігається з вашим обліковим записом.',
+ 'flash_logged_out' => 'Ви вийшли з інших пристроїв.',
+ ],
+ 'password' => [
+ 'update_title' => 'Оновити пароль',
+ 'set_title' => 'Встановити пароль',
+ 'update_description' => 'Переконайтеся, що ваш обліковий запис використовує довгий випадковий пароль для безпеки.',
+ 'set_description' => 'Додайте пароль, щоб входити без підключеного провайдера.',
+ 'current_password' => 'Поточний пароль',
+ 'new_password' => 'Новий пароль',
+ 'confirm_password' => 'Підтвердіть пароль',
+ 'save' => 'Зберегти пароль',
+ 'set' => 'Встановити пароль',
+ ],
+ 'providers' => [
+ 'title' => 'Підключені акаунти',
+ 'description' => 'Входьте швидше через ці підключені провайдери.',
+ 'connected' => 'Підключено',
+ 'not_connected' => 'Не підключено',
+ 'connect' => 'Підключити',
+ 'disconnect' => 'Від’єднати',
+ 'flash_disconnected' => ':provider успішно від’єднано.',
+ 'flash_connected' => ':provider успішно підключено.',
+ 'flash_already_linked' => 'Цей акаунт :provider уже прив’язано до іншого користувача.',
+ 'flash_cannot_disconnect' => 'Ви не можете від’єднати свій єдиний спосіб входу. Спочатку встановіть пароль або підключіть іншого провайдера.',
+ ],
+ ],
+
+ 'delete_account' => [
+ 'heading' => 'Видалити обліковий запис',
+ 'description' => 'Видаліть обліковий запис і всі його ресурси',
+ 'warning' => 'Увага',
+ 'warning_message' => 'Будьте обережні: цю дію не можна скасувати. Якщо ви власник цього облікового запису, запрошених учасників також буде назавжди видалено.',
+ 'button' => 'Видалити обліковий запис',
+ 'modal_title' => 'Ви впевнені, що хочете видалити обліковий запис?',
+ 'modal_description_password' => 'Після видалення облікового запису всі його ресурси та дані також будуть назавжди видалені. Якщо ви власник облікового запису, запрошених учасників також буде назавжди видалено. Введіть пароль для підтвердження.',
+ 'modal_description_email' => 'Після видалення облікового запису всі його ресурси та дані також будуть назавжди видалені. Якщо ви власник облікового запису, запрошених учасників також буде назавжди видалено. Введіть свою адресу email :email для підтвердження.',
+ 'password' => 'Пароль',
+ 'password_placeholder' => 'Пароль',
+ 'email_placeholder' => 'Email вашого облікового запису',
+ 'email_mismatch' => 'Адреса email не збігається з вашим обліковим записом.',
+ 'cancel' => 'Скасувати',
+ 'confirm' => 'Видалити обліковий запис',
+ ],
+
+ 'workspace' => [
+ 'tabs' => [
+ 'workspace' => 'Робочий простір',
+ 'brand' => 'Бренд',
+ 'users' => 'Учасники',
+ 'api_keys' => 'API-ключі',
+ ],
+ 'title' => 'Налаштування робочого простору',
+ 'logo_heading' => 'Логотип робочого простору',
+ 'logo_description' => 'Завантажте логотип для робочого простору',
+ 'heading' => 'Назва робочого простору',
+ 'description' => 'Оновіть назву робочого простору',
+ 'members_heading' => 'Учасники',
+ 'members_description' => 'Керуйте учасниками та запрошеннями робочого простору',
+ 'name' => 'Назва',
+ 'name_placeholder' => 'Мій робочий простір',
+ 'save' => 'Зберегти',
+ 'danger_description' => 'Незворотні дії.',
+ 'delete_warning' => 'Увага',
+ 'delete_title' => 'Видалити цей workspace',
+ 'delete_description' => 'Назавжди видаляє цей workspace разом із постами, підключеними акаунтами та медіа. З вас стягуватиметься оплата на один workspace менше.',
+ 'delete_description_self_hosted' => 'Назавжди видаляє цей workspace, його пости, підключені акаунти та медіа.',
+ 'delete_only_description' => 'Ви не можете видалити свій єдиний workspace. Скасуйте підписку в розділі оплати, щоб перестати платити, або видаліть обліковий запис в Автентифікації, щоб скасувати оплату та назавжди видалити все.',
+ 'delete_go_to_billing' => 'Перейти до оплати',
+ 'delete_go_to_delete_account' => 'Видалити обліковий запис',
+ 'delete_members_warning' => '{1}:count інший учасник втратить доступ. Учасники без іншого workspace у TryPost будуть назавжди видалені.|[2,*]:count інших учасників втратять доступ. Учасники без іншого workspace у TryPost будуть назавжди видалені.',
+ 'delete_action' => 'Видалити workspace',
+ 'delete_cancel' => 'Скасувати',
+ 'delete_confirm_title' => 'Видалити workspace?',
+ 'delete_confirm_description' => 'Це назавжди видалить workspace і всі його дані. Оплату буде оновлено з урахуванням одного workspace менше.',
+ 'delete_confirm_description_self_hosted' => 'Це назавжди видалить workspace і всі його дані.',
+ ],
+
+ 'brand' => [
+ 'title' => 'Бренд',
+ 'description' => 'Налаштуйте ідентичність бренду для AI-контенту.',
+ 'name' => 'Назва робочого простору',
+ 'name_placeholder' => 'Мій бренд',
+ 'website' => 'Вебсайт',
+ 'website_placeholder' => 'https://yourbrand.com',
+ 'brand_description' => 'Опис',
+ 'brand_description_placeholder' => 'Розкажіть про ваш бренд, чим ви займаєтеся та хто ваша аудиторія...',
+ 'voice' => 'Голос бренду',
+ 'voice_description' => 'Виберіть риси, що визначають звучання вашого контенту.',
+ 'voice_group' => [
+ 'pov' => 'Точка зору',
+ 'formality' => 'Формальність',
+ 'energy' => 'Енергія',
+ 'humor' => 'Гумор',
+ 'attitude' => 'Настрій',
+ 'warmth' => 'Теплота',
+ 'confidence' => 'Впевненість',
+ 'style' => 'Стиль',
+ ],
+ 'voice_trait' => [
+ 'first_person' => 'Перша особа',
+ 'second_person' => 'Друга особа (ви)',
+ 'third_person' => 'Третя особа',
+ 'formal' => 'Формальний',
+ 'balanced' => 'Збалансований',
+ 'casual' => 'Неформальний',
+ 'calm' => 'Спокійний',
+ 'moderate' => 'Помірний',
+ 'enthusiastic' => 'Ентузіастичний',
+ 'vibrant' => 'Яскравий',
+ 'serious' => 'Серйозний',
+ 'dry' => 'Сухий / тонкий',
+ 'witty' => 'Дотепний',
+ 'playful' => 'Грайливий',
+ 'respectful' => 'Шанобливий',
+ 'even_handed' => 'Об’єктивний',
+ 'bold' => 'Сміливий',
+ 'provocative' => 'Провокаційний',
+ 'neutral' => 'Нейтральний',
+ 'friendly' => 'Дружній',
+ 'empathetic' => 'Емпатичний',
+ 'humble' => 'Скромний',
+ 'confident' => 'Впевнений',
+ 'assertive' => 'Наполегливий',
+ 'direct' => 'Прямий',
+ 'concise' => 'Стислий',
+ 'transparent' => 'Прозорий',
+ 'no_hype' => 'Без хайпу',
+ 'practical' => 'Практичний',
+ 'data_driven' => 'На основі даних',
+ 'storytelling' => 'Оповідний',
+ 'inspirational' => 'Надихаючий',
+ 'educational' => 'Освітній',
+ 'technical' => 'Технічний',
+ 'minimalist' => 'Мало emoji',
+ ],
+ 'brand_color' => 'Колір бренду',
+ 'background_color' => 'Колір фону',
+ 'text_color' => 'Колір тексту',
+ 'font' => 'Шрифт',
+ 'image_style' => 'Стиль зображення',
+ 'image_style_description' => 'Візуальний стиль для AI-слайдів та обкладинок постів.',
+ 'image_style_cinematic' => 'Кінематографічний',
+ 'image_style_illustration' => 'Ілюстрація',
+ 'image_style_isometric_3d' => 'Ізометричний',
+ 'image_style_cartoon' => 'Мультяшний',
+ 'image_style_typographic' => 'Типографічний',
+ 'image_style_infographic' => 'Інфографіка',
+ 'image_style_minimalist' => 'Мінімалістичний',
+ 'image_style_mockup' => 'Мокап',
+ 'content_language' => 'Мова контенту',
+ 'content_language_description' => 'Мова для AI-підписів, хештегів і будь-якого тексту всередині згенерованих зображень або відео.',
+ 'font_placeholder' => 'Виберіть шрифт…',
+ 'font_search' => 'Пошук шрифту…',
+ 'font_empty' => 'Шрифтів не знайдено.',
+ 'language_placeholder' => 'Виберіть мову…',
+ 'language_search' => 'Пошук мови…',
+ 'language_empty' => 'Мову не знайдено.',
+
+ ],
+
+ 'members' => [
+ 'title' => 'Учасники',
+ 'heading' => 'Учасники команди',
+ 'description' => 'Керуйте учасниками та запрошеннями цього робочого простору',
+
+ 'cancel' => 'Скасувати',
+ 'remove' => 'Видалити',
+ 'make_role' => 'Призначити :role',
+
+ 'invite' => [
+ 'title' => 'Запросити учасника',
+ 'description' => 'Надішліть email-запрошення, щоб додати співавторів',
+ 'email' => 'Email',
+ 'email_placeholder' => 'collaborator@email.com',
+ 'role' => 'Роль',
+ 'role_placeholder' => 'Виберіть роль',
+ 'submit' => 'Надіслати запрошення',
+ ],
+
+ 'pending' => [
+ 'title' => 'Очікуючі запрошення',
+ 'description' => 'Запрошення, що очікують прийняття',
+ 'empty' => 'Немає очікуючих запрошень',
+ ],
+
+ 'list' => [
+ 'title' => 'Учасники',
+ 'description' => 'Люди з доступом до цього робочого простору',
+ 'empty' => 'Немає учасників, окрім власника',
+ ],
+
+ 'remove_modal' => [
+ 'title' => 'Видалити учасника',
+ 'description' => 'Ви впевнені, що хочете видалити цього учасника з робочого простору? Він втратить доступ до всіх ресурсів робочого простору.',
+ 'action' => 'Видалити учасника',
+ ],
+
+ 'cancel_invite_modal' => [
+ 'title' => 'Скасувати запрошення',
+ 'description' => 'Ви впевнені, що хочете скасувати це запрошення?',
+ 'action' => 'Скасувати запрошення',
+ ],
+
+ 'roles' => [
+ 'owner' => 'Власник',
+ 'admin' => 'Адміністратор',
+ 'member' => 'Учасник',
+ 'viewer' => 'Глядач',
+ ],
+
+ 'errors' => [
+ 'invite_exists' => 'Запрошення для цієї адреси email уже існує.',
+ 'email_belongs_to_account' => 'Цей email уже належить іншому обліковому запису TryPost. Попросіть робочу адресу email.',
+ ],
+
+ 'flash' => [
+ 'invite_sent' => 'Запрошення успішно надіслано!',
+ 'invite_deleted' => 'Запрошення видалено.',
+ 'member_removed' => 'Учасника успішно видалено.',
+ 'role_updated' => 'Роль учасника оновлено.',
+ 'wrong_email' => 'Це запрошення для іншої адреси email.',
+ 'already_member' => 'Ви вже є учасником цього робочого простору.',
+ 'invite_accepted' => 'Ласкаво просимо! Тепер ви учасник робочого простору.',
+ 'invite_workspace_gone' => 'Це запрошення більше не дійсне, оскільки робочий простір було видалено.',
+ 'invite_declined' => 'Запрошення відхилено.',
+ ],
+ ],
+
+ 'account' => [
+ 'tabs' => [
+ 'account' => 'Обліковий запис',
+ 'usage' => 'Використання',
+ 'billing' => 'Оплата',
+ ],
+ 'title' => 'Налаштування облікового запису',
+ 'description' => 'Керуйте назвою облікового запису та email для оплати',
+ 'name' => 'Назва облікового запису',
+ 'name_placeholder' => 'Моя компанія',
+ 'billing_email' => 'Email для оплати',
+ 'billing_email_placeholder' => 'billing@company.com',
+ 'billing_email_hint' => 'Цей email використовуватиметься для рахунків і повідомлень про оплату від Stripe.',
+ 'submit' => 'Зберегти',
+ ],
+
+ 'flash' => [
+ 'account_updated' => 'Обліковий запис успішно оновлено!',
+ 'profile_updated' => 'Профіль успішно оновлено!',
+ 'password_updated' => 'Пароль успішно оновлено!',
+ 'workspace_updated' => 'Налаштування успішно оновлено!',
+ 'photo_updated' => 'Фото успішно оновлено!',
+ 'photo_deleted' => 'Фото успішно видалено!',
+ 'delete_failed_billing' => 'Не вдалося скасувати підписку у платіжного провайдера. Нічого не видалено. Спробуйте ще раз або зверніться до підтримки.',
+ 'logo_updated' => 'Логотип успішно завантажено!',
+ 'logo_deleted' => 'Логотип успішно видалено!',
+ 'notifications_updated' => 'Налаштування сповіщень оновлено!',
+ ],
+
+ 'api_keys' => [
+ 'title' => 'API-ключі',
+ 'page_title' => 'API-ключі',
+ 'heading' => 'API-ключі',
+ 'description' => 'Керуйте API-ключами для програмного доступу до робочого простору.',
+ 'create' => 'Створити API-ключ',
+ 'copy' => 'Копіювати',
+ 'new_token_message' => 'Ваш новий API-ключ створено. Скопіюйте його зараз — ви більше не зможете його побачити.',
+ 'table' => [
+ 'name' => 'Назва',
+ 'key' => 'Ключ',
+ 'status' => 'Статус',
+ 'expires' => 'Діє до',
+ 'last_used' => 'Останнє використання',
+ 'never' => 'Ніколи',
+ ],
+ 'actions' => [
+ 'copy_id' => 'Копіювати ID API-ключа',
+ 'copy_id_success' => 'ID API-ключа скопійовано в буфер обміну',
+ 'delete' => 'Видалити',
+ ],
+ 'empty' => [
+ 'title' => 'API-ключів ще немає',
+ 'description' => 'Створіть API-ключ для програмного доступу до робочого простору.',
+ ],
+ 'delete_modal' => [
+ 'title' => 'Видалити API-ключ',
+ 'description' => 'Ви впевнені, що хочете видалити цей API-ключ? Усі додатки, що використовують цей ключ, негайно втратять доступ.',
+ 'action' => 'Видалити API-ключ',
+ ],
+ 'create_dialog' => [
+ 'title' => 'Створити API-ключ',
+ 'description' => 'Створіть новий API-ключ для програмного доступу до робочого простору.',
+ 'name' => 'Назва',
+ 'name_placeholder' => 'напр. Production API Key',
+ 'expires' => 'Дата закінчення (необов’язково)',
+ 'expires_placeholder' => 'Без терміну дії',
+ 'submit' => 'Створити',
+ 'cancel' => 'Скасувати',
+ ],
+ 'flash' => [
+ 'created' => 'API-ключ успішно створено!',
+ 'deleted' => 'API-ключ успішно видалено!',
+ ],
+ ],
+];
diff --git a/lang/uk/sidebar.php b/lang/uk/sidebar.php
new file mode 100644
index 000000000..08ed5216f
--- /dev/null
+++ b/lang/uk/sidebar.php
@@ -0,0 +1,61 @@
+ 'Робочі простори',
+ 'select_workspace' => 'Виберіть робочий простір',
+ 'create_workspace' => 'Створити робочий простір',
+ 'create_post' => 'Створити пост',
+ 'profile' => 'Профіль',
+ 'log_out' => 'Вийти',
+
+ 'workspace' => 'Робочий простір: :name',
+ 'workspace_select' => 'Робочий простір: вибрати',
+
+ 'theme' => 'Тема: :name',
+ 'theme_light' => 'Світла',
+ 'theme_dark' => 'Темна',
+ 'theme_system' => 'Системна',
+
+ 'language' => 'Мова: :name',
+ 'language_select' => 'Мова: вибрати',
+
+ 'groups' => [
+ 'posts' => 'Пости',
+ 'workspace' => 'Робочий простір',
+ 'others' => 'Інше',
+ ],
+
+ 'analytics' => 'Аналітика',
+ 'automations' => 'Автоматизації',
+ 'settings' => 'Налаштування',
+
+ 'posts' => [
+ 'calendar' => 'Календар',
+ 'all' => 'Усі',
+ 'scheduled' => 'Заплановані',
+ 'posted' => 'Опубліковані',
+ 'drafts' => 'Чернетки',
+ ],
+
+ 'workspace' => [
+ 'connections' => 'Підключення',
+ 'signatures' => 'Підписи',
+ 'labels' => 'Мітки',
+ 'assets' => 'Медіафайли',
+ 'api_keys' => 'API-ключі',
+ ],
+
+ 'notifications' => 'Сповіщення',
+ 'mark_all_read' => 'Позначити все прочитаним',
+ 'mark_as_read' => 'Позначити прочитаним',
+ 'archive_all' => 'Архівувати все',
+ 'no_notifications' => 'Немає сповіщень',
+
+ 'support' => [
+ 'docs' => 'Документація',
+ 'referral' => 'Отримуйте 30% за рефералами',
+ 'stay_updated' => 'Слідкуйте за оновленнями',
+ ],
+];
diff --git a/lang/uk/signatures.php b/lang/uk/signatures.php
new file mode 100644
index 000000000..4f1e32513
--- /dev/null
+++ b/lang/uk/signatures.php
@@ -0,0 +1,59 @@
+ 'Підписи',
+ 'description' => 'Створюйте багаторазові підписи для швидкого додавання до постів',
+ 'search' => 'Пошук підписів...',
+ 'new' => 'Новий підпис',
+ 'empty_title' => 'Підписів ще немає',
+ 'empty_description' => 'Створюйте підписи, щоб швидко додавати хештеги, посилання або будь-який текст, який ви повторюєте',
+ 'no_search_results' => 'Жоден підпис не відповідає вашому пошуку',
+ 'try_different_search' => 'Спробуйте інше ключове слово або очистіть пошук.',
+ 'table' => [
+ 'name' => 'Назва',
+ 'content' => 'Вміст',
+ 'created_at' => 'Створено',
+ ],
+
+ 'actions' => [
+ 'edit' => 'Редагувати підпис',
+ 'delete' => 'Видалити підпис',
+ ],
+
+ 'create' => [
+ 'title' => 'Створити підпис',
+ 'description' => 'Дайте підпису назву та вміст для додавання (хештеги, посилання, власний текст — усе, що ви повторюєте).',
+ 'name' => 'Назва',
+ 'name_placeholder' => 'напр. Маркетинг, Подорожі, Підпис бренду',
+ 'content' => 'Вміст',
+ 'content_placeholder' => "#marketing #socialmedia\nДізнайтеся більше: https://yourbrand.com",
+ 'content_hint' => 'Хештеги, посилання, вступи, підписи — усе, що ви додаєте до постів.',
+ 'submit' => 'Створити підпис',
+ 'submitting' => 'Створення...',
+ ],
+
+ 'edit' => [
+ 'title' => 'Редагувати підпис',
+ 'description' => 'Оновіть назву та вміст цього підпису.',
+ 'name' => 'Назва',
+ 'name_placeholder' => 'напр. Маркетинг, Подорожі, Підпис бренду',
+ 'content' => 'Вміст',
+ 'content_placeholder' => "#marketing #socialmedia\nДізнайтеся більше: https://yourbrand.com",
+ 'content_hint' => 'Хештеги, посилання, вступи, підписи — усе, що ви додаєте до постів.',
+ 'submit' => 'Зберегти зміни',
+ 'submitting' => 'Збереження...',
+ ],
+
+ 'delete' => [
+ 'title' => 'Видалити підпис',
+ 'description' => 'Ви впевнені, що хочете видалити цей підпис? Цю дію не можна скасувати.',
+ 'confirm' => 'Видалити',
+ 'cancel' => 'Скасувати',
+ ],
+
+ 'flash' => [
+ 'created' => 'Підпис створено.',
+ 'updated' => 'Підпис оновлено.',
+ 'deleted' => 'Підпис видалено.',
+ ],
+];
diff --git a/lang/uk/usage.php b/lang/uk/usage.php
new file mode 100644
index 000000000..39bf104f7
--- /dev/null
+++ b/lang/uk/usage.php
@@ -0,0 +1,15 @@
+ 'Використання',
+
+ 'section_account' => 'Обліковий запис',
+ 'section_account_description' => 'Квоти та обмеження вашого тарифного плану.',
+ 'section_ai' => 'AI-кредити',
+ 'section_ai_description' => 'Кредити списуються під час використання AI-функцій. Вони оновлюються першого числа кожного місяця.',
+
+ 'workspaces' => 'Робочі простори',
+ 'social_accounts' => 'Соціальні акаунти',
+ 'members' => 'Учасники',
+ 'credits' => 'Кредити',
+];
diff --git a/lang/uk/validation.php b/lang/uk/validation.php
new file mode 100644
index 000000000..310c8aae0
--- /dev/null
+++ b/lang/uk/validation.php
@@ -0,0 +1,200 @@
+ 'Поле :attribute має бути прийняте.',
+ 'accepted_if' => 'Поле :attribute має бути прийняте, коли :other дорівнює :value.',
+ 'active_url' => 'Поле :attribute має містити коректну URL-адресу.',
+ 'after' => 'Поле :attribute має містити дату після :date.',
+ 'after_or_equal' => 'Поле :attribute має містити дату не раніше :date.',
+ 'alpha' => 'Поле :attribute має містити лише літери.',
+ 'alpha_dash' => 'Поле :attribute має містити лише літери, цифри, дефіси та підкреслення.',
+ 'alpha_num' => 'Поле :attribute має містити лише літери та цифри.',
+ 'any_of' => 'Поле :attribute недійсне.',
+ 'array' => 'Поле :attribute має бути масивом.',
+ 'ascii' => 'Поле :attribute має містити лише однобайтові латинські символи та знаки.',
+ 'before' => 'Поле :attribute має містити дату до :date.',
+ 'before_or_equal' => 'Поле :attribute має містити дату не пізніше :date.',
+ 'between' => [
+ 'array' => 'Поле :attribute має містити від :min до :max елементів.',
+ 'file' => 'Розмір файлу в полі :attribute має бути від :min до :max кілобайт.',
+ 'numeric' => 'Поле :attribute має бути від :min до :max.',
+ 'string' => 'Кількість символів у полі :attribute має бути від :min до :max.',
+ ],
+ 'boolean' => 'Поле :attribute має мати значення true або false.',
+ 'can' => 'Поле :attribute містить недопустиме значення.',
+ 'confirmed' => 'Підтвердження поля :attribute не збігається.',
+ 'contains' => 'У полі :attribute відсутнє обов’язкове значення.',
+ 'current_password' => 'Невірний пароль.',
+ 'date' => 'Поле :attribute має містити коректну дату.',
+ 'date_equals' => 'Поле :attribute має містити дату, що дорівнює :date.',
+ 'date_format' => 'Поле :attribute має відповідати формату :format.',
+ 'decimal' => 'Поле :attribute має містити :decimal знаків після коми.',
+ 'declined' => 'Поле :attribute має бути відхилене.',
+ 'declined_if' => 'Поле :attribute має бути відхилене, коли :other дорівнює :value.',
+ 'different' => 'Поля :attribute та :other мають відрізнятися.',
+ 'digits' => 'Поле :attribute має містити :digits цифр.',
+ 'digits_between' => 'Поле :attribute має містити від :min до :max цифр.',
+ 'dimensions' => 'Поле :attribute має недопустимі розміри зображення.',
+ 'distinct' => 'Поле :attribute містить повторюване значення.',
+ 'doesnt_contain' => 'Поле :attribute не має містити жодного з таких значень: :values.',
+ 'doesnt_end_with' => 'Поле :attribute не має закінчуватися одним із таких значень: :values.',
+ 'doesnt_start_with' => 'Поле :attribute не має починатися з одного з таких значень: :values.',
+ 'email' => 'Поле :attribute має містити коректну адресу email.',
+ 'encoding' => 'Поле :attribute має бути в кодуванні :encoding.',
+ 'ends_with' => 'Поле :attribute має закінчуватися одним із таких значень: :values.',
+ 'enum' => 'Вибране значення для :attribute недійсне.',
+ 'exists' => 'Вибране значення для :attribute недійсне.',
+ 'extensions' => 'Поле :attribute має мати одне з таких розширень: :values.',
+ 'file' => 'Поле :attribute має бути файлом.',
+ 'filled' => 'Поле :attribute має мати значення.',
+ 'gt' => [
+ 'array' => 'Поле :attribute має містити більше ніж :value елементів.',
+ 'file' => 'Розмір файлу в полі :attribute має бути більше :value кілобайт.',
+ 'numeric' => 'Поле :attribute має бути більше :value.',
+ 'string' => 'Кількість символів у полі :attribute має бути більше :value.',
+ ],
+ 'gte' => [
+ 'array' => 'Поле :attribute має містити :value елементів або більше.',
+ 'file' => 'Розмір файлу в полі :attribute має бути більше або дорівнювати :value кілобайт.',
+ 'numeric' => 'Поле :attribute має бути більше або дорівнювати :value.',
+ 'string' => 'Кількість символів у полі :attribute має бути більше або дорівнювати :value.',
+ ],
+ 'hex_color' => 'Поле :attribute має містити коректний шістнадцятковий колір.',
+ 'image' => 'Поле :attribute має бути зображенням.',
+ 'in' => 'Вибране значення для :attribute недійсне.',
+ 'in_array' => 'Поле :attribute має існувати в :other.',
+ 'in_array_keys' => 'Поле :attribute має містити хоча б один з таких ключів: :values.',
+ 'integer' => 'Поле :attribute має бути цілим числом.',
+ 'ip' => 'Поле :attribute має містити коректну IP-адресу.',
+ 'ipv4' => 'Поле :attribute має містити коректну IPv4-адресу.',
+ 'ipv6' => 'Поле :attribute має містити коректну IPv6-адресу.',
+ 'json' => 'Поле :attribute має містити коректний рядок JSON.',
+ 'list' => 'Поле :attribute має бути списком.',
+ 'lowercase' => 'Поле :attribute має бути в нижньому регістрі.',
+ 'lt' => [
+ 'array' => 'Поле :attribute має містити менше ніж :value елементів.',
+ 'file' => 'Розмір файлу в полі :attribute має бути менше :value кілобайт.',
+ 'numeric' => 'Поле :attribute має бути менше :value.',
+ 'string' => 'Кількість символів у полі :attribute має бути менше :value.',
+ ],
+ 'lte' => [
+ 'array' => 'Поле :attribute не має містити більше ніж :value елементів.',
+ 'file' => 'Розмір файлу в полі :attribute має бути менше або дорівнювати :value кілобайт.',
+ 'numeric' => 'Поле :attribute має бути менше або дорівнювати :value.',
+ 'string' => 'Кількість символів у полі :attribute має бути менше або дорівнювати :value.',
+ ],
+ 'mac_address' => 'Поле :attribute має містити коректну MAC-адресу.',
+ 'max' => [
+ 'array' => 'Поле :attribute не має містити більше ніж :max елементів.',
+ 'file' => 'Розмір файлу в полі :attribute не має перевищувати :max кілобайт.',
+ 'numeric' => 'Поле :attribute не має бути більше :max.',
+ 'string' => 'Кількість символів у полі :attribute не має перевищувати :max.',
+ ],
+ 'max_digits' => 'Поле :attribute не має містити більше ніж :max цифр.',
+ 'mimes' => 'Поле :attribute має бути файлом одного з типів: :values.',
+ 'mimetypes' => 'Поле :attribute має бути файлом одного з типів: :values.',
+ 'min' => [
+ 'array' => 'Поле :attribute має містити щонайменше :min елементів.',
+ 'file' => 'Розмір файлу в полі :attribute має бути щонайменше :min кілобайт.',
+ 'numeric' => 'Поле :attribute має бути щонайменше :min.',
+ 'string' => 'Кількість символів у полі :attribute має бути щонайменше :min.',
+ ],
+ 'min_digits' => 'Поле :attribute має містити щонайменше :min цифр.',
+ 'missing' => 'Поле :attribute має бути відсутнім.',
+ 'missing_if' => 'Поле :attribute має бути відсутнім, коли :other дорівнює :value.',
+ 'missing_unless' => 'Поле :attribute має бути відсутнім, якщо :other не дорівнює :value.',
+ 'missing_with' => 'Поле :attribute має бути відсутнім, коли присутнє :values.',
+ 'missing_with_all' => 'Поле :attribute має бути відсутнім, коли присутні :values.',
+ 'multiple_of' => 'Поле :attribute має бути кратним :value.',
+ 'not_in' => 'Вибране значення для :attribute недійсне.',
+ 'not_regex' => 'Формат поля :attribute недійсний.',
+ 'numeric' => 'Поле :attribute має бути числом.',
+ 'password' => [
+ 'letters' => 'Поле :attribute має містити щонайменше одну літеру.',
+ 'mixed' => 'Поле :attribute має містити щонайменше одну велику та одну малу літеру.',
+ 'numbers' => 'Поле :attribute має містити щонайменше одну цифру.',
+ 'symbols' => 'Поле :attribute має містити щонайменше один символ.',
+ 'uncompromised' => 'Вказане значення :attribute з’являлося у витоку даних. Будь ласка, оберіть інше значення :attribute.',
+ ],
+ 'present' => 'Поле :attribute має бути присутнім.',
+ 'present_if' => 'Поле :attribute має бути присутнім, коли :other дорівнює :value.',
+ 'present_unless' => 'Поле :attribute має бути присутнім, якщо :other не дорівнює :value.',
+ 'present_with' => 'Поле :attribute має бути присутнім, коли присутнє :values.',
+ 'present_with_all' => 'Поле :attribute має бути присутнім, коли присутні :values.',
+ 'prohibited' => 'Поле :attribute заборонене.',
+ 'prohibited_if' => 'Поле :attribute заборонене, коли :other дорівнює :value.',
+ 'prohibited_if_accepted' => 'Поле :attribute заборонене, коли :other прийняте.',
+ 'prohibited_if_declined' => 'Поле :attribute заборонене, коли :other відхилене.',
+ 'prohibited_unless' => 'Поле :attribute заборонене, якщо :other не входить до :values.',
+ 'prohibits' => 'Поле :attribute забороняє наявність :other.',
+ 'regex' => 'Формат поля :attribute недійсний.',
+ 'required' => 'Поле :attribute обов’язкове.',
+ 'required_array_keys' => 'Поле :attribute має містити записи для: :values.',
+ 'required_if' => 'Поле :attribute обов’язкове, коли :other дорівнює :value.',
+ 'required_if_accepted' => 'Поле :attribute обов’язкове, коли :other прийняте.',
+ 'required_if_declined' => 'Поле :attribute обов’язкове, коли :other відхилене.',
+ 'required_unless' => 'Поле :attribute обов’язкове, якщо :other не входить до :values.',
+ 'required_with' => 'Поле :attribute обов’язкове, коли присутнє :values.',
+ 'required_with_all' => 'Поле :attribute обов’язкове, коли присутні :values.',
+ 'required_without' => 'Поле :attribute обов’язкове, коли :values відсутнє.',
+ 'required_without_all' => 'Поле :attribute обов’язкове, коли жодне з :values не присутнє.',
+ 'same' => 'Поле :attribute має збігатися з :other.',
+ 'size' => [
+ 'array' => 'Поле :attribute має містити :size елементів.',
+ 'file' => 'Розмір файлу в полі :attribute має бути :size кілобайт.',
+ 'numeric' => 'Поле :attribute має дорівнювати :size.',
+ 'string' => 'Кількість символів у полі :attribute має дорівнювати :size.',
+ ],
+ 'starts_with' => 'Поле :attribute має починатися з одного з таких значень: :values.',
+ 'string' => 'Поле :attribute має бути рядком.',
+ 'timezone' => 'Поле :attribute має містити коректний часовий пояс.',
+ 'unique' => 'Таке значення поля :attribute вже зайняте.',
+ 'uploaded' => 'Не вдалося завантажити :attribute.',
+ 'uppercase' => 'Поле :attribute має бути у верхньому регістрі.',
+ 'url' => 'Поле :attribute має містити коректну URL-адресу.',
+ 'ulid' => 'Поле :attribute має містити коректний ULID.',
+ 'uuid' => 'Поле :attribute має містити коректний UUID.',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Language Lines
+ |--------------------------------------------------------------------------
+ |
+ | Here you may specify custom validation messages for attributes using the
+ | convention "attribute.rule" to name the lines. This makes it quick to
+ | specify a specific custom language line for a given attribute rule.
+ |
+ */
+
+ 'custom' => [
+ 'attribute-name' => [
+ 'rule-name' => 'custom-message',
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Custom Validation Attributes
+ |--------------------------------------------------------------------------
+ |
+ | The following language lines are used to swap our attribute placeholder
+ | with something more reader friendly such as "E-Mail Address" instead
+ | of "email". This simply helps us make our message more expressive.
+ |
+ */
+
+ 'attributes' => [],
+
+];
diff --git a/lang/uk/workspaces.php b/lang/uk/workspaces.php
new file mode 100644
index 000000000..50f2487dc
--- /dev/null
+++ b/lang/uk/workspaces.php
@@ -0,0 +1,50 @@
+ 'Робочі простори',
+ 'select_title' => 'Ваші робочі простори',
+ 'select_description' => 'Виберіть робочий простір, щоб продовжити',
+ 'current' => 'Поточний',
+ 'connections' => ':count підключень',
+ 'posts' => ':count постів',
+
+ 'create' => [
+ 'page_title' => 'Створіть свій робочий простір',
+ 'title' => 'Налаштуйте робочий простір',
+ 'description' => 'Розкажіть трохи про себе або свій проєкт. Ми використаємо це, щоб адаптувати AI-пости під ваш стиль.',
+ 'website' => 'Вебсайт',
+ 'website_placeholder' => 'https://yourbrand.com',
+ 'autofill' => 'Заповнити з вебсайту',
+ 'autofill_missing_url' => 'Спочатку введіть URL.',
+ 'autofill_success' => 'Інформацію про бренд завантажено.',
+ 'autofill_error' => 'Не вдалося автозаповнити. Ви можете заповнити поля вручну.',
+ 'autofill_errors' => [
+ 'unreachable' => 'Не вдалося отримати доступ до цього вебсайту (:reason).',
+ 'http_status' => 'Вебсайт повернув неочікуваний статус (:status).',
+ 'invalid_scheme' => 'Підтримуються лише URL з http та https.',
+ 'missing_host' => 'У URL відсутній хост.',
+ 'unresolvable_host' => 'Не вдалося розв’язати хост (:host).',
+ 'private_network' => 'URL, що вказують на приватні мережі, не дозволені.',
+ ],
+ 'logo_captured' => 'Логотип отримано з вашого вебсайту.',
+ 'name' => 'Назва робочого простору',
+ 'name_placeholder' => 'напр. Acme Inc',
+ 'brand_description' => 'Опис бренду',
+ 'brand_description_placeholder' => 'Чим займається ваш бренд?',
+ 'content_language' => 'Мова контенту',
+ 'content_language_description' => 'AI-підписи будуть написані цією мовою.',
+ 'brand_color' => 'Колір бренду',
+ 'background_color' => 'Колір фону',
+ 'text_color' => 'Колір тексту',
+ 'submit' => 'Створити робочий простір',
+ 'success' => 'Робочий простір створено. Підключіть соціальний акаунт, щоб почати публікувати.',
+ ],
+
+ 'cannot_delete_last' => 'Ви не можете видалити свій єдиний робочий простір. Скасуйте підписку в налаштуваннях оплати, щоб закрити обліковий запис.',
+
+ 'flash' => [
+ 'deleted' => 'Робочий простір успішно видалено.',
+ ],
+];
diff --git a/resources/js/dayjs.ts b/resources/js/dayjs.ts
index 1f4b1e8e1..235cdd773 100644
--- a/resources/js/dayjs.ts
+++ b/resources/js/dayjs.ts
@@ -13,6 +13,7 @@ import weekday from 'dayjs/plugin/weekday';
// Import locales
import 'dayjs/locale/en';
+import 'dayjs/locale/uk';
import 'dayjs/locale/es';
import 'dayjs/locale/pt-br';
import 'dayjs/locale/fr';
@@ -42,7 +43,7 @@ dayjs.extend(weekday);
dayjs.extend(isBetween);
// Set Monday as first day of week (to match Carbon/Laravel)
-const weekStartMonday = ['en', 'es', 'pt-br', 'fr', 'de', 'it', 'nl', 'pl', 'el', 'ja', 'ko', 'zh', 'ru', 'tr', 'ar'];
+const weekStartMonday = ['en', 'uk', 'es', 'pt-br', 'fr', 'de', 'it', 'nl', 'pl', 'el', 'ja', 'ko', 'zh', 'ru', 'tr', 'ar'];
weekStartMonday.forEach((locale) => dayjs.updateLocale(locale, { weekStart: 1 }));
export default dayjs;
diff --git a/tests/Feature/Ai/AutofillBrandTest.php b/tests/Feature/Ai/AutofillBrandTest.php
index f0eec7c73..bb13d9f74 100644
--- a/tests/Feature/Ai/AutofillBrandTest.php
+++ b/tests/Feature/Ai/AutofillBrandTest.php
@@ -102,6 +102,7 @@
})->with([
// Every supported language, detected from its primary subtag.
['en', 'en'],
+ ['uk', 'uk'],
['pt', 'pt-BR'],
['es', 'es'],
['fr', 'fr'],
@@ -119,6 +120,7 @@
// Region/script subtags still resolve to the supported language.
['pt-PT', 'pt-BR'],
['en-US', 'en'],
+ ['uk-UA', 'uk'],
['es-MX', 'es'],
['ja-JP', 'ja'],
['zh-Hans', 'zh'],
diff --git a/tests/Feature/Middleware/SetLocaleTest.php b/tests/Feature/Middleware/SetLocaleTest.php
index 1da03f036..be2f1bba9 100644
--- a/tests/Feature/Middleware/SetLocaleTest.php
+++ b/tests/Feature/Middleware/SetLocaleTest.php
@@ -38,7 +38,7 @@ function runSetLocale(?string $locale): string
test('shares ltr direction for a left-to-right locale', function (string $locale) {
expect(runSetLocale($locale))->toBe('ltr');
expect(app()->getLocale())->toBe($locale);
-})->with(['en', 'ja', 'pt-BR', 'de']);
+})->with(['en', 'uk', 'ja', 'pt-BR', 'de']);
test('falls back to the default locale and ltr for an unknown cookie', function () {
expect(runSetLocale('sv'))->toBe('ltr');
diff --git a/tests/Feature/Settings/ProfileUpdateTest.php b/tests/Feature/Settings/ProfileUpdateTest.php
index d9ea1a3a7..7778becf1 100644
--- a/tests/Feature/Settings/ProfileUpdateTest.php
+++ b/tests/Feature/Settings/ProfileUpdateTest.php
@@ -78,6 +78,22 @@
$response->assertCookieNotExpired('locale');
});
+test('user can switch the UI locale to Ukrainian', function () {
+ $user = User::factory()->create();
+
+ $response = $this
+ ->actingAs($user)
+ ->from(route('app.posts.index'))
+ ->put(route('app.profile.language'), [
+ 'locale' => 'uk',
+ ]);
+
+ $response
+ ->assertSessionHasNoErrors()
+ ->assertRedirect(route('app.posts.index'))
+ ->assertCookieNotExpired('locale');
+});
+
test('user cannot update locale with invalid code', function () {
$user = User::factory()->create();
diff --git a/tests/Feature/WorkspaceControllerTest.php b/tests/Feature/WorkspaceControllerTest.php
index aa09fb86d..55ad9a586 100644
--- a/tests/Feature/WorkspaceControllerTest.php
+++ b/tests/Feature/WorkspaceControllerTest.php
@@ -62,6 +62,7 @@
->component('workspaces/Create', false)
->has('availableContentLanguages', count(ContentLanguage::cases()))
->where('availableContentLanguages.0', ['value' => 'en', 'label' => 'English', 'englishName' => 'English'])
+ ->where('availableContentLanguages.1', ['value' => 'uk', 'label' => 'Українська', 'englishName' => 'Ukrainian'])
);
});
@@ -313,6 +314,7 @@
->component('settings/workspace/Brand', false)
->has('workspace')
->has('availableContentLanguages', count(ContentLanguage::cases()))
+ ->where('availableContentLanguages.1', ['value' => 'uk', 'label' => 'Українська', 'englishName' => 'Ukrainian'])
);
});
@@ -426,6 +428,18 @@
expect($this->workspace->refresh()->content_language)->toBe('fr');
});
+test('update workspace settings persists Ukrainian as a content language', function () {
+ $this->actingAs($this->user)
+ ->from(route('app.workspace.brand'))
+ ->put(route('app.workspace.settings.update'), [
+ 'name' => $this->workspace->name,
+ 'content_language' => 'uk',
+ ])->assertRedirect(route('app.workspace.brand'))
+ ->assertSessionHasNoErrors();
+
+ expect($this->workspace->refresh()->content_language)->toBe('uk');
+});
+
test('update workspace settings rejects an unsupported content language', function () {
$this->actingAs($this->user)
->put(route('app.workspace.settings.update'), [
diff --git a/tests/Unit/Ai/Agents/PostContentGeneratorTest.php b/tests/Unit/Ai/Agents/PostContentGeneratorTest.php
index b00476851..5a42c1568 100644
--- a/tests/Unit/Ai/Agents/PostContentGeneratorTest.php
+++ b/tests/Unit/Ai/Agents/PostContentGeneratorTest.php
@@ -4,9 +4,28 @@
use App\Ai\Agents\PostContentGenerator;
use App\Enums\Ai\GeneratorFormat;
+use App\Enums\Workspace\ContentLanguage;
use App\Models\Workspace;
use Illuminate\JsonSchema\JsonSchemaTypeFactory;
+test('instructions use English for the default content language', function () {
+ $workspace = Workspace::factory()->make([
+ 'content_language' => ContentLanguage::DEFAULT->value,
+ ]);
+
+ expect((new PostContentGenerator(workspace: $workspace))->instructions())
+ ->toContain('Write the output in the language with code: en.');
+});
+
+test('instructions use Ukrainian when the workspace content language is uk', function () {
+ $workspace = Workspace::factory()->make([
+ 'content_language' => ContentLanguage::Ukrainian->value,
+ ]);
+
+ expect((new PostContentGenerator(workspace: $workspace))->instructions())
+ ->toContain('Write the output in the language with code: uk.');
+});
+
test('instructions render brand context', function () {
$workspace = Workspace::factory()->make([
'name' => 'TryPost',
diff --git a/tests/Unit/Ai/Agents/PostContentHumanizerTest.php b/tests/Unit/Ai/Agents/PostContentHumanizerTest.php
index 4d3763ff4..25f353481 100644
--- a/tests/Unit/Ai/Agents/PostContentHumanizerTest.php
+++ b/tests/Unit/Ai/Agents/PostContentHumanizerTest.php
@@ -36,3 +36,10 @@
expect($agent->instructions())->toContain('AI-tells');
});
+
+test('instructions use Ukrainian when the workspace content language is uk', function () {
+ $workspace = Workspace::factory()->make(['content_language' => 'uk']);
+
+ expect((new PostContentHumanizer(workspace: $workspace))->instructions())
+ ->toContain('Output language: uk.');
+});
diff --git a/tests/Unit/Ai/Agents/PostContentReviewerTest.php b/tests/Unit/Ai/Agents/PostContentReviewerTest.php
index a99413928..3586cbc85 100644
--- a/tests/Unit/Ai/Agents/PostContentReviewerTest.php
+++ b/tests/Unit/Ai/Agents/PostContentReviewerTest.php
@@ -22,6 +22,15 @@
expect($instructions)->toContain('pt-BR');
});
+test('instructions use Ukrainian when the workspace content language is uk', function () {
+ $workspace = Workspace::factory()->make([
+ 'content_language' => 'uk',
+ ]);
+
+ expect((new PostContentReviewer(workspace: $workspace))->instructions())
+ ->toContain('Output language: uk.');
+});
+
test('schema requires suggestions array with original/suggestion/reason', function () {
$workspace = Workspace::factory()->make();
$agent = new PostContentReviewer(workspace: $workspace);
diff --git a/tests/Unit/Enums/PersonaTest.php b/tests/Unit/Enums/PersonaTest.php
index b0eb2c382..def0c2fe7 100644
--- a/tests/Unit/Enums/PersonaTest.php
+++ b/tests/Unit/Enums/PersonaTest.php
@@ -3,6 +3,7 @@
declare(strict_types=1);
use App\Enums\User\Persona;
+use App\Enums\Workspace\ContentLanguage;
test('persona values are stable', function () {
expect(array_map(fn (Persona $persona): string => $persona->value, Persona::cases()))
@@ -15,4 +16,4 @@
expect(__($key, [], $locale))->not->toBe($key);
}
-})->with(['en', 'es', 'pt-BR']);
+})->with(ContentLanguage::values());
diff --git a/tests/Unit/Enums/Workspace/ContentLanguageTest.php b/tests/Unit/Enums/Workspace/ContentLanguageTest.php
index b816c52e7..0e9a9e6fa 100644
--- a/tests/Unit/Enums/Workspace/ContentLanguageTest.php
+++ b/tests/Unit/Enums/Workspace/ContentLanguageTest.php
@@ -6,12 +6,12 @@
test('values exposes every supported content-language code', function () {
expect(ContentLanguage::values())->toBe([
- 'en', 'pt-BR', 'es', 'fr', 'de', 'it', 'nl',
+ 'en', 'uk', 'pt-BR', 'es', 'fr', 'de', 'it', 'nl',
'pl', 'el', 'ja', 'ko', 'zh', 'ru', 'tr', 'ar',
]);
});
-test('default language is English', function () {
+test('default content language is English', function () {
expect(ContentLanguage::DEFAULT)->toBe(ContentLanguage::English);
expect(ContentLanguage::DEFAULT->value)->toBe('en');
});
@@ -21,6 +21,7 @@
expect($options)->toHaveCount(count(ContentLanguage::cases()));
expect($options[0])->toBe(['value' => 'en', 'label' => 'English', 'englishName' => 'English']);
+ expect($options)->toContain(['value' => 'uk', 'label' => 'Українська', 'englishName' => 'Ukrainian']);
expect($options)->toContain(['value' => 'pt-BR', 'label' => 'Português (Brasil)', 'englishName' => 'Brazilian Portuguese']);
expect($options)->toContain(['value' => 'ja', 'label' => '日本語', 'englishName' => 'Japanese']);
});
@@ -29,6 +30,7 @@
expect($language->englishName())->toBe($expected);
})->with([
[ContentLanguage::English, 'English'],
+ [ContentLanguage::Ukrainian, 'Ukrainian'],
[ContentLanguage::PortugueseBrazil, 'Brazilian Portuguese'],
[ContentLanguage::Spanish, 'Spanish'],
[ContentLanguage::French, 'French'],
@@ -55,6 +57,7 @@
expect($language->label())->toBe($expected);
})->with([
[ContentLanguage::English, 'English'],
+ [ContentLanguage::Ukrainian, 'Українська'],
[ContentLanguage::PortugueseBrazil, 'Português (Brasil)'],
[ContentLanguage::Spanish, 'Español'],
[ContentLanguage::French, 'Français'],
@@ -87,6 +90,7 @@
['pt', ContentLanguage::PortugueseBrazil],
['pt-PT', ContentLanguage::PortugueseBrazil],
['en-US', ContentLanguage::English],
+ ['uk-UA', ContentLanguage::Ukrainian],
['es-MX', ContentLanguage::Spanish],
['fr', ContentLanguage::French],
['ja-JP', ContentLanguage::Japanese],
diff --git a/tests/Unit/Services/Ai/AiImageClientTest.php b/tests/Unit/Services/Ai/AiImageClientTest.php
index 5b65f2bd8..6537aae9b 100644
--- a/tests/Unit/Services/Ai/AiImageClientTest.php
+++ b/tests/Unit/Services/Ai/AiImageClientTest.php
@@ -90,6 +90,15 @@
Image::assertGenerated(fn (ImagePrompt $prompt) => $prompt->contains('French'));
});
+test('generate appends Ukrainian instruction when language is uk', function () {
+ Image::fake();
+
+ $client = new AiImageClient;
+ $client->generate(['x'], ImageStyle::Cinematic, language: 'uk');
+
+ Image::assertGenerated(fn (ImagePrompt $prompt) => $prompt->contains('Ukrainian'));
+});
+
test('generate defaults to English instruction when language is unsupported', function () {
Image::fake();
diff --git a/tests/Unit/Settings/DeleteAccountCopyTest.php b/tests/Unit/Settings/DeleteAccountCopyTest.php
index 6b9d29c5f..3fc683e23 100644
--- a/tests/Unit/Settings/DeleteAccountCopyTest.php
+++ b/tests/Unit/Settings/DeleteAccountCopyTest.php
@@ -13,6 +13,7 @@
*/
$accountDeleteInvitedMemberMarkers = [
'en' => 'invited members',
+ 'uk' => 'запрошених учасників',
'pt-BR' => 'membros convidados',
'es' => 'miembros invitados',
'fr' => 'membres invités',
@@ -34,6 +35,7 @@
*/
$workspaceDeleteConditionalMemberMarkers = [
'en' => 'without another TryPost workspace',
+ 'uk' => 'без іншого workspace у TryPost',
'pt-BR' => 'sem outro workspace no TryPost',
'es' => 'sin otro workspace en TryPost',
'fr' => 'sans autre workspace TryPost',