Add Ukrainian as a supported platform language (#219)

* Default AI content language to Ukrainian

* Keep English as the default content language

* refactor: update content language handling in workspace creation

- Changed the default content language in CreateWorkspace to inherit the app's locale instead of defaulting to English.
- Updated related tests to reflect this change, ensuring that the content language aligns with the application's current locale settings.
- Cleaned up unnecessary code in the BrandTab component for better readability.

* Add Ukrainian as a full platform UI locale.

Wire uk into languages config with complete lang/uk translations and restore ContentLanguage↔UI parity checks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Wire Ukrainian into dayjs and locale coverage tests.

Import the uk dayjs locale with Monday week-start and cover uk/uk-UA in brand autofill and SetLocale assertions.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Harden Ukrainian UI and AI content-language coverage.

Cover generator/reviewer/humanizer/image prompts, workspace pickers, persona labels, UI locale switch, and README for uk as a first-class platform language.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Ukrainian Pinterest form strings after main merge.

Restore lang/uk posts.php key parity for the new pin title and destination link fields.

Co-authored-by: Cursor <cursoragent@cursor.com>

* List every supported UI language in the README.

Replace the abbreviated multi-language blurb with the full locale set from config.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Axi 2026-08-06 01:45:30 +03:00 committed by GitHub
parent e52d1d7b25
commit 3baf2e9c41
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 2769 additions and 6 deletions

View file

@ -54,7 +54,7 @@ ## Features
| **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

View file

@ -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',

View file

@ -17,6 +17,7 @@
'available' => [
'en' => 'English',
'uk' => 'Українська',
'es' => 'Español',
'pt-BR' => 'Português',
'fr' => 'Français',

148
lang/uk/accounts.php Normal file
View file

@ -0,0 +1,148 @@
<?php
return [
'title' => 'Підключення',
'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' => 'З міркувань безпеки використовуйте <strong>пароль додатка</strong>. Створіть його на <a href="https://bsky.app/settings/app-passwords" target="_blank" class="underline">bsky.app/settings</a>.',
'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.',
],
];

55
lang/uk/analytics.php Normal file
View file

@ -0,0 +1,55 @@
<?php
return [
'no_accounts' => 'Немає підключених акаунтів з аналітикою.',
'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' => 'Перегляди',
],
];

52
lang/uk/assets.php Normal file
View file

@ -0,0 +1,52 @@
<?php
return [
'title' => 'Медіафайли',
'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',
],
];

144
lang/uk/auth.php Normal file
View file

@ -0,0 +1,144 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'Ці облікові дані не відповідають нашим записам.',
'password' => 'Наданий пароль неправильний.',
'throttle' => 'Забагато спроб входу. Спробуйте ще раз через :seconds сек.',
'flash' => [
'welcome' => 'Ласкаво просимо до TryPost!',
'welcome_trial' => 'Ласкаво просимо до TryPost! Ваш пробний період розпочато.',
],
'legal' => 'Продовжуючи, ви погоджуєтеся з нашими <a href="https://trypost.it/terms" target="_blank">Умовами використання</a> та <a href="https://trypost.it/privacy" target="_blank">Політикою конфіденційності</a>.',
'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' => 'На головну',
],
];

413
lang/uk/automations.php Normal file
View file

@ -0,0 +1,413 @@
<?php
declare(strict_types=1);
return [
'mobile_notice' => 'Редактор автоматизацій найкраще працює на великому екрані. Відкрийте його на комп’ютері, щоб побудувати 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 генерує лише зображення. Виберіть формат зображення (не відео).',
],
];

76
lang/uk/billing.php Normal file
View file

@ -0,0 +1,76 @@
<?php
return [
'title' => 'Оплата',
'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' => 'Спробувати ще раз',
],
];

39
lang/uk/brands.php Normal file
View file

@ -0,0 +1,39 @@
<?php
return [
'new_brand' => 'Новий бренд',
'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' => 'Бренд успішно видалено!',
],
];

12
lang/uk/calendar.php Normal file
View file

@ -0,0 +1,12 @@
<?php
return [
'title' => 'Календар',
'today' => 'Сьогодні',
'day' => 'День',
'week' => 'Тиждень',
'month' => 'Місяць',
'new_post' => 'Новий пост',
'no_content' => 'Немає контенту',
'more' => '+:count ще',
];

18
lang/uk/comments.php Normal file
View file

@ -0,0 +1,18 @@
<?php
return [
'placeholder' => 'Напишіть коментар...',
'reply_placeholder' => 'Напишіть відповідь...',
'reply' => 'Відповісти',
'edit' => 'Редагувати',
'delete' => 'Видалити',
'edited' => 'змінено',
'save' => 'Зберегти',
'cancel' => 'Скасувати',
'send' => 'Надіслати',
'replying_to' => 'Відповідь для :name',
'empty' => 'Коментарів ще немає. Почніть розмову.',
'load_more' => 'Завантажити старіші коментарі',
'today' => 'Сьогодні',
'yesterday' => 'Вчора',
];

67
lang/uk/common.php Normal file
View file

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
return [
'back' => 'Назад',
'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' => 'Не вдалося скопіювати в буфер обміну',
],
];

54
lang/uk/labels.php Normal file
View file

@ -0,0 +1,54 @@
<?php
return [
'title' => 'Мітки',
'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' => 'Мітку успішно видалено!',
],
];

22
lang/uk/mail.php Normal file
View file

@ -0,0 +1,22 @@
<?php
return [
'mentioned' => [
'subject' => ':name згадав(ла) вас у TryPost',
'title' => ':name згадав(ла) вас',
'intro' => ':name згадав(ла) вас у коментарі до поста.',
'cta' => 'Переглянути коментар',
],
'workspace_connections_disconnected' => [
'subject' => '{1} :count акаунт потрібно перепідключити в :workspace|[2,*] :count акаунтів потрібно перепідключити в :workspace',
'title' => 'Акаунти потребують перепідключення',
'intro' => 'Наступні соціальні акаунти у вашому робочому просторі <strong>:workspace</strong> було від’єднано, і їх потрібно перепідключити:',
'reasons_title' => 'Це могло статися через:',
'reason_expired' => 'Термін дії токенів доступу минув',
'reason_revoked' => 'Ви відкликали доступ TryPost на платформі',
'reason_changed' => 'Платформа змінила вимоги до автентифікації',
'reconnect_cta' => 'Перепідключіть ці акаунти, щоб продовжити планування та публікацію постів.',
'button' => 'Перепідключити акаунти',
],
];

18
lang/uk/notifications.php Normal file
View file

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
return [
'post_ready' => [
'title' => 'Ваш пост готовий',
'body' => 'AI щойно завершив роботу. Натисніть, щоб переглянути та опублікувати.',
],
'account_disconnected' => [
'title' => 'Акаунт :platform від’єднано',
'body' => ':account потрібно перепідключити',
],
'account_token_expired' => [
'title' => 'Акаунт :platform потрібно перепідключити',
'body' => 'Сесію :account завершено — перепідключіть, щоб продовжити публікацію',
],
];

57
lang/uk/onboarding.php Normal file
View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
return [
'title' => 'Ласкаво просимо до 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' => 'Підключіть хоча б одну мережу, щоб продовжити.',
],
];

19
lang/uk/pagination.php Normal file
View file

@ -0,0 +1,19 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Pagination Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used by the paginator library to build
| the simple pagination links. You are free to change them to anything
| you want to customize your views to better match your application.
|
*/
'previous' => '&laquo; Попередня',
'next' => 'Наступна &raquo;',
];

22
lang/uk/passwords.php Normal file
View file

@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Password Reset Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are the default lines which match reasons
| that are given by the password broker for a password update attempt
| outcome such as failure due to an invalid password / reset token.
|
*/
'reset' => 'Ваш пароль було скинуто.',
'sent' => 'Ми надіслали посилання для скидання пароля на вашу електронну пошту.',
'throttled' => 'Зачекайте, перш ніж повторити спробу.',
'token' => 'Цей токен скидання пароля недійсний.',
'user' => 'Ми не знайшли користувача з такою адресою електронної пошти.',
];

690
lang/uk/posts.php Normal file
View file

@ -0,0 +1,690 @@
<?php
return [
'title' => 'Пости',
'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' => 'Карусель із кількох зображень (25 зображень)',
],
'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' => 'Залучення',
],
],
];

384
lang/uk/settings.php Normal file
View file

@ -0,0 +1,384 @@
<?php
return [
'title' => 'Налаштування',
'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-ключ успішно видалено!',
],
],
];

61
lang/uk/sidebar.php Normal file
View file

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
return [
'workspaces' => 'Робочі простори',
'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' => 'Слідкуйте за оновленнями',
],
];

59
lang/uk/signatures.php Normal file
View file

@ -0,0 +1,59 @@
<?php
return [
'title' => 'Підписи',
'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' => 'Підпис видалено.',
],
];

15
lang/uk/usage.php Normal file
View file

@ -0,0 +1,15 @@
<?php
return [
'title' => 'Використання',
'section_account' => 'Обліковий запис',
'section_account_description' => 'Квоти та обмеження вашого тарифного плану.',
'section_ai' => 'AI-кредити',
'section_ai_description' => 'Кредити списуються під час використання AI-функцій. Вони оновлюються першого числа кожного місяця.',
'workspaces' => 'Робочі простори',
'social_accounts' => 'Соціальні акаунти',
'members' => 'Учасники',
'credits' => 'Кредити',
];

200
lang/uk/validation.php Normal file
View file

@ -0,0 +1,200 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'Поле :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' => [],
];

50
lang/uk/workspaces.php Normal file
View file

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
return [
'title' => 'Робочі простори',
'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' => 'Робочий простір успішно видалено.',
],
];

View file

@ -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;

View file

@ -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'],

View file

@ -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');

View file

@ -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();

View file

@ -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'), [

View file

@ -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',

View file

@ -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.');
});

View file

@ -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);

View file

@ -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());

View file

@ -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],

View file

@ -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();

View file

@ -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',