2026-01-21 23:58:08 +00:00
< ? php
return [
'title' => 'Posts' ,
2026-03-31 00:18:07 +00:00
'search' => 'Search posts...' ,
2026-01-21 23:58:08 +00:00
'all_posts' => 'All Posts' ,
'new_post' => 'New Post' ,
'no_posts' => 'No posts found' ,
2026-05-02 15:22:42 +00:00
'no_search_results' => 'No posts match your search' ,
'try_different_search' => 'Try a different keyword or clear the search.' ,
2026-01-21 23:58:08 +00:00
'start_creating' => 'Start by creating your first post.' ,
feat(posts): multi-select label filter on the posts list
Adds a combobox-style filter to the posts index toolbar so users can
narrow All / Scheduled / Posted / Drafts views by one or more labels.
- `PostController::index` accepts `?labels[]=<id>` and applies
`whereHas('labels', whereIn(...))` (OR semantics across selected labels).
Workspace labels are exposed to the page (sorted by name) and the
selected set comes back under `filters.labels`.
- New `LabelFilter.vue` component reuses the existing Popover + Command
pattern (matching `FontPicker` in the Brand settings page). Trigger
renders the selected `LabelBadge`s inline (mirroring how each post row
already displays its labels): 1-3 shown directly, 4+ shown as the
first three plus a "+N" overflow indicator. Clear button has a
tooltip and `cursor-pointer`, and stops `click`/`pointerdown`/
`mousedown` so it doesn't reopen the Popover.
- Existing search debounce is shared with the new label watcher via a
single `buildFilterUrl` helper. URL is updated with `preserveState +
replace` so the back stack stays clean.
- i18n in en / pt-BR / es: `filter_by_label`, `label_search_placeholder`,
`no_labels`, `clear_label_filter`.
Tests: 4 new index tests covering the labels prop exposure, single-label
filter, multi-label OR filter, and blank-id sanitization. Full suite:
1509 passed, 2 skipped, 0 failed.
2026-05-14 12:57:55 +00:00
'filter_by_label' => 'Filter by label' ,
'label_search_placeholder' => 'Search labels...' ,
'no_labels' => 'No labels found.' ,
'clear_label_filter' => 'Clear label filter' ,
2026-05-02 15:22:42 +00:00
'table' => [
2026-05-06 17:26:18 +00:00
'post' => 'Post' ,
2026-05-02 15:22:42 +00:00
'status' => 'Status' ,
'content' => 'Content' ,
'platforms' => 'Platforms' ,
'labels' => 'Labels' ,
'scheduled_at' => 'Date' ,
'actions' => '' ,
],
2026-01-21 23:58:08 +00:00
'manage_posts' => 'Manage all your posts' ,
'delete_confirm' => 'Are you sure you want to delete this post?' ,
'by' => 'by' ,
2026-01-22 00:33:31 +00:00
'actions' => [
'view' => 'View post' ,
2026-05-04 01:37:51 +00:00
'delete' => 'Delete' ,
'duplicate' => 'Duplicate' ,
'copy_id' => 'Copy ID' ,
'copied' => 'ID copied to clipboard' ,
2026-01-22 00:33:31 +00:00
],
2026-01-22 00:46:27 +00:00
'form' => [
'post_type' => 'Post Type' ,
2026-01-22 00:49:45 +00:00
'board' => 'Board' ,
'select_board' => 'Select a board' ,
'search_board' => 'Search board...' ,
'no_board_found' => 'No board found' ,
'media' => 'Media' ,
'min' => 'Min' ,
'uploading' => 'Uploading...' ,
'drop_to_upload' => 'Drop to upload' ,
'drag_and_drop' => 'Drag & drop or click to upload' ,
'photos_and_videos' => 'Photos and videos' ,
'photos_only' => 'Photos only' ,
'videos_only' => 'Videos only' ,
'drag_to_reorder' => 'Drag to reorder' ,
'caption' => 'Caption' ,
'write_caption' => 'Write your caption...' ,
2026-05-11 22:39:41 +00:00
'content_exceeds_platform' => ':platform: too long by :over chars (max :limit).' ,
2026-04-02 20:57:06 +00:00
'tiktok' => [
'settings' => 'TikTok Settings' ,
feat(tiktok): photo carousel support + UX Content Sharing API compliance
## Photo carousel support
- Adds `ContentType::TikTokPhoto` enum case (max 35 photos, 1:1 aspect,
supportsImage true, supportsVideo false) and JS mirror in content-type.ts.
- Variant pill picker (Video / Photo carousel) at the top of TikTokSettings,
mirroring the Instagram pattern. Wired through ScheduleTab to the parent
editor's existing update:platformContentType emit.
- i18n keys for variant_label / variant.video / variant.photo in en/pt-BR/es.
- Publisher: split buildPostInfo into buildVideoPostInfo (uses `title`,
TikTok cap 2200 chars) and buildPhotoPostInfo (uses `description`, cap
4000 chars; omits Duet/Stitch/AIGC since they don't apply). Removed the
no-longer-needed queryCreatorInfo() call from publishVideo/publishPhotos
— its only previous consumer (silent privacy_level fallback) is gone.
## UX Content Sharing API compliance
Per TikTok review feedback citing
https://developers.tiktok.com/doc/content-sharing-guidelines#required_ux_implementation_in_your_app
Point 1 — already satisfied (creator_info fetch + nickname display).
Point 2/4 — Music Usage Confirmation declaration is now always visible
in TikTokSettings; text changes between "Music Usage Confirmation" and
"Branded Content Policy and Music Usage Confirmation" based on toggle
state. Previously the entire `<p>` block was conditional on a brand
toggle being selected, hiding the baseline declaration.
Point 2b — privacy_level may not have a default. UI was already correct;
backend hardened: UpdatePostRequest now requires meta.privacy_level for
tiktok platforms when status is publishing/scheduled (via withValidator);
TikTokPublisher::resolveRequiredPrivacyLevel throws TikTokPublishException
(ContentPolicy category) when missing instead of silently falling back to
the creator's preferred level.
Point 2c — interaction settings now condition on content type:
- Photo posts hide Duet/Stitch (they don't apply per TikTok docs).
- Photo posts hide AIGC (also video-only).
- Video posts hide Auto Add Music (photos-only feature).
- Max-duration warning hidden when not a video post.
Source of truth is the user-selected contentType prop, not inferred
from media — ensures the UI reacts immediately to the variant pill.
Point 3a — publish button stays disabled when Disclose toggle is on
without a sub-selection (already the case via tiktokComplianceValid).
The disabled tooltip now uses the verbatim TikTok-required text "You
need to indicate if your content promotes yourself, a third party, or
both." instead of the generic "Some platform settings are incomplete..."
when the only blocker is TikTok disclosure incompleteness.
Point 3b — SELF_ONLY (Only me) privacy option is no longer filtered out
when Branded Content is checked. It is rendered disabled with a hover
tooltip "Branded content visibility cannot be set to private." plus a
persistent amber warning paragraph below the dropdown. When the user
toggles Branded Content while privacy is SELF_ONLY, the privacy clears
and a vue-sonner warning toast surfaces the change.
## Cross-cutting
- New `resources/js/enums/platform.ts` mirrors the PHP Platform enum,
used in Edit.vue (tiktokComplianceValid + tiktokDisclosureIncomplete)
and ScheduleTab.vue (all selected*Platforms computeds) to replace
string literal comparisons against `'tiktok'` / `'facebook'` / etc.
- PostPlatformFactory tiktok() state defaults meta.privacy_level to
SELF_ONLY so existing test fixtures keep passing under the new
publisher/FormRequest requirements.
## Tests
- New tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php
covering the new enum case (4 tests).
- TikTokPublisherTest: added "video uses title not description" and
"throws when meta.privacy_level missing" regression tests; renamed
two existing tests that depended on the removed silent fallback.
- New tests/Feature/UpdatePostRequestTest.php with 3 tests covering
the FormRequest's privacy_level enforcement (publish-rejected,
publish-passes, draft-allowed).
Full Pest suite: 1490 passed, 2 skipped (pre-existing).
2026-05-09 15:47:49 +00:00
'variant_label' => 'Post type' ,
'variant' => [
'video' => 'Video' ,
'photo' => 'Photo carousel' ,
],
2026-04-23 16:23:24 +00:00
'posting_to' => 'Posting to' ,
2026-04-02 20:57:06 +00:00
'privacy_level' => 'Who can see this video?' ,
2026-04-23 16:23:24 +00:00
'privacy_placeholder' => 'Select visibility' ,
2026-04-02 20:57:06 +00:00
'privacy' => [
'public' => 'Public to everyone' ,
'friends' => 'Mutual follow friends' ,
'followers' => 'Followers' ,
'private' => 'Only me' ,
feat(tiktok): photo carousel support + UX Content Sharing API compliance
## Photo carousel support
- Adds `ContentType::TikTokPhoto` enum case (max 35 photos, 1:1 aspect,
supportsImage true, supportsVideo false) and JS mirror in content-type.ts.
- Variant pill picker (Video / Photo carousel) at the top of TikTokSettings,
mirroring the Instagram pattern. Wired through ScheduleTab to the parent
editor's existing update:platformContentType emit.
- i18n keys for variant_label / variant.video / variant.photo in en/pt-BR/es.
- Publisher: split buildPostInfo into buildVideoPostInfo (uses `title`,
TikTok cap 2200 chars) and buildPhotoPostInfo (uses `description`, cap
4000 chars; omits Duet/Stitch/AIGC since they don't apply). Removed the
no-longer-needed queryCreatorInfo() call from publishVideo/publishPhotos
— its only previous consumer (silent privacy_level fallback) is gone.
## UX Content Sharing API compliance
Per TikTok review feedback citing
https://developers.tiktok.com/doc/content-sharing-guidelines#required_ux_implementation_in_your_app
Point 1 — already satisfied (creator_info fetch + nickname display).
Point 2/4 — Music Usage Confirmation declaration is now always visible
in TikTokSettings; text changes between "Music Usage Confirmation" and
"Branded Content Policy and Music Usage Confirmation" based on toggle
state. Previously the entire `<p>` block was conditional on a brand
toggle being selected, hiding the baseline declaration.
Point 2b — privacy_level may not have a default. UI was already correct;
backend hardened: UpdatePostRequest now requires meta.privacy_level for
tiktok platforms when status is publishing/scheduled (via withValidator);
TikTokPublisher::resolveRequiredPrivacyLevel throws TikTokPublishException
(ContentPolicy category) when missing instead of silently falling back to
the creator's preferred level.
Point 2c — interaction settings now condition on content type:
- Photo posts hide Duet/Stitch (they don't apply per TikTok docs).
- Photo posts hide AIGC (also video-only).
- Video posts hide Auto Add Music (photos-only feature).
- Max-duration warning hidden when not a video post.
Source of truth is the user-selected contentType prop, not inferred
from media — ensures the UI reacts immediately to the variant pill.
Point 3a — publish button stays disabled when Disclose toggle is on
without a sub-selection (already the case via tiktokComplianceValid).
The disabled tooltip now uses the verbatim TikTok-required text "You
need to indicate if your content promotes yourself, a third party, or
both." instead of the generic "Some platform settings are incomplete..."
when the only blocker is TikTok disclosure incompleteness.
Point 3b — SELF_ONLY (Only me) privacy option is no longer filtered out
when Branded Content is checked. It is rendered disabled with a hover
tooltip "Branded content visibility cannot be set to private." plus a
persistent amber warning paragraph below the dropdown. When the user
toggles Branded Content while privacy is SELF_ONLY, the privacy clears
and a vue-sonner warning toast surfaces the change.
## Cross-cutting
- New `resources/js/enums/platform.ts` mirrors the PHP Platform enum,
used in Edit.vue (tiktokComplianceValid + tiktokDisclosureIncomplete)
and ScheduleTab.vue (all selected*Platforms computeds) to replace
string literal comparisons against `'tiktok'` / `'facebook'` / etc.
- PostPlatformFactory tiktok() state defaults meta.privacy_level to
SELF_ONLY so existing test fixtures keep passing under the new
publisher/FormRequest requirements.
## Tests
- New tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php
covering the new enum case (4 tests).
- TikTokPublisherTest: added "video uses title not description" and
"throws when meta.privacy_level missing" regression tests; renamed
two existing tests that depended on the removed silent fallback.
- New tests/Feature/UpdatePostRequestTest.php with 3 tests covering
the FormRequest's privacy_level enforcement (publish-rejected,
publish-passes, draft-allowed).
Full Pest suite: 1490 passed, 2 skipped (pre-existing).
2026-05-09 15:47:49 +00:00
'private_disabled_branded' => 'Branded content visibility cannot be set to private.' ,
2026-04-02 20:57:06 +00:00
],
'privacy_hint' => 'The available options depend on your TikTok account settings.' ,
'auto_add_music' => 'Auto add music' ,
'auto_add_music_hint' => 'This feature is available only for photos. It will add a default music that you can change later.' ,
'yes' => 'Yes' ,
'no' => 'No' ,
'allow_users' => 'Allow users to:' ,
'comments' => 'Comment' ,
'duet' => 'Duet' ,
'stitch' => 'Stitch' ,
'is_aigc' => 'Video made with AI' ,
2026-04-23 16:23:24 +00:00
'disclose' => 'Disclose video content' ,
'disclose_hint' => 'Turn on to disclose that this video promotes goods or services in exchange for something of value. Your video could promote yourself, a third party, or both.' ,
'promotional_organic_title' => 'Your photo/video will be labeled as "Promotional content".' ,
'promotional_paid_title' => 'Your photo/video will be labeled as "Paid partnership".' ,
'promotional_description' => 'This cannot be changed once your video is posted.' ,
'compliance_incomplete' => 'You need to indicate if your content promotes yourself, a third party, or both.' ,
2026-05-09 16:56:00 +00:00
'privacy_required' => 'TikTok privacy level is required when publishing.' ,
feat(tiktok): photo carousel support + UX Content Sharing API compliance
## Photo carousel support
- Adds `ContentType::TikTokPhoto` enum case (max 35 photos, 1:1 aspect,
supportsImage true, supportsVideo false) and JS mirror in content-type.ts.
- Variant pill picker (Video / Photo carousel) at the top of TikTokSettings,
mirroring the Instagram pattern. Wired through ScheduleTab to the parent
editor's existing update:platformContentType emit.
- i18n keys for variant_label / variant.video / variant.photo in en/pt-BR/es.
- Publisher: split buildPostInfo into buildVideoPostInfo (uses `title`,
TikTok cap 2200 chars) and buildPhotoPostInfo (uses `description`, cap
4000 chars; omits Duet/Stitch/AIGC since they don't apply). Removed the
no-longer-needed queryCreatorInfo() call from publishVideo/publishPhotos
— its only previous consumer (silent privacy_level fallback) is gone.
## UX Content Sharing API compliance
Per TikTok review feedback citing
https://developers.tiktok.com/doc/content-sharing-guidelines#required_ux_implementation_in_your_app
Point 1 — already satisfied (creator_info fetch + nickname display).
Point 2/4 — Music Usage Confirmation declaration is now always visible
in TikTokSettings; text changes between "Music Usage Confirmation" and
"Branded Content Policy and Music Usage Confirmation" based on toggle
state. Previously the entire `<p>` block was conditional on a brand
toggle being selected, hiding the baseline declaration.
Point 2b — privacy_level may not have a default. UI was already correct;
backend hardened: UpdatePostRequest now requires meta.privacy_level for
tiktok platforms when status is publishing/scheduled (via withValidator);
TikTokPublisher::resolveRequiredPrivacyLevel throws TikTokPublishException
(ContentPolicy category) when missing instead of silently falling back to
the creator's preferred level.
Point 2c — interaction settings now condition on content type:
- Photo posts hide Duet/Stitch (they don't apply per TikTok docs).
- Photo posts hide AIGC (also video-only).
- Video posts hide Auto Add Music (photos-only feature).
- Max-duration warning hidden when not a video post.
Source of truth is the user-selected contentType prop, not inferred
from media — ensures the UI reacts immediately to the variant pill.
Point 3a — publish button stays disabled when Disclose toggle is on
without a sub-selection (already the case via tiktokComplianceValid).
The disabled tooltip now uses the verbatim TikTok-required text "You
need to indicate if your content promotes yourself, a third party, or
both." instead of the generic "Some platform settings are incomplete..."
when the only blocker is TikTok disclosure incompleteness.
Point 3b — SELF_ONLY (Only me) privacy option is no longer filtered out
when Branded Content is checked. It is rendered disabled with a hover
tooltip "Branded content visibility cannot be set to private." plus a
persistent amber warning paragraph below the dropdown. When the user
toggles Branded Content while privacy is SELF_ONLY, the privacy clears
and a vue-sonner warning toast surfaces the change.
## Cross-cutting
- New `resources/js/enums/platform.ts` mirrors the PHP Platform enum,
used in Edit.vue (tiktokComplianceValid + tiktokDisclosureIncomplete)
and ScheduleTab.vue (all selected*Platforms computeds) to replace
string literal comparisons against `'tiktok'` / `'facebook'` / etc.
- PostPlatformFactory tiktok() state defaults meta.privacy_level to
SELF_ONLY so existing test fixtures keep passing under the new
publisher/FormRequest requirements.
## Tests
- New tests/Unit/Enums/PostPlatform/TikTokPhotoContentTypeTest.php
covering the new enum case (4 tests).
- TikTokPublisherTest: added "video uses title not description" and
"throws when meta.privacy_level missing" regression tests; renamed
two existing tests that depended on the removed silent fallback.
- New tests/Feature/UpdatePostRequestTest.php with 3 tests covering
the FormRequest's privacy_level enforcement (publish-rejected,
publish-passes, draft-allowed).
Full Pest suite: 1490 passed, 2 skipped (pre-existing).
2026-05-09 15:47:49 +00:00
'branded_cleared_private' => 'Privacy was cleared because Branded Content cannot be private.' ,
2026-04-23 16:23:24 +00:00
'interaction_disabled_by_creator' => 'Disabled by your TikTok account settings.' ,
'max_duration_exceeded' => 'Video is :duration s long but this account can only post videos up to :max s.' ,
'processing_hint' => 'After publishing, it may take a few minutes for the content to process and appear on your TikTok profile.' ,
'brand_organic' => 'Your brand' ,
'brand_organic_hint' => 'You are promoting yourself or your own brand. This video will be classified as Brand Organic.' ,
'brand_content' => 'Branded content' ,
'brand_content_hint' => 'You are promoting another brand or a third party. This video will be classified as Branded Content.' ,
'compliance' => [
'agree' => " By posting, you agree to TikTok's " ,
'music_usage' => 'Music Usage Confirmation' ,
'and' => 'and' ,
'branded_policy' => 'Branded Content Policy' ,
],
2026-04-02 20:57:06 +00:00
],
feat: media gallery picker, custom emoji picker, preview tabs, real platform logos
- gallery: extract /assets tabs (uploads, Unsplash, Giphy) into shared
GalleryBrowser used by both /assets and a new MediaPickerDialog inside the
post editor; add JSON search endpoint for workspace assets with tests
- emoji: replace broken emoji-picker-element web component with a custom
EmojiPicker (full Unicode set, search, categories, recently-used,
light/dark, i18n)
- preview tab: platform selector pills, variant tabs (data-driven from
content_types map) so the user can switch Feed/Reel/Story etc. and have
it autosave through the same handler ScheduleTab uses
- platform logos: shared usePlatformLogo composable (logo + label + content
types); replaces inline maps across 5 components, fixes
instagram-facebook falling back to default.png
- tooltips: hover details (display_name · @username + platform label) on
platform avatars across editor, posts list and calendar
- settings cards: show ` · @username` in the title bar so multiple accounts
on the same network are distinguishable
- routes: drop the throttle:6,1 group middleware on social connect routes
(was 429ing legitimate OAuth retries) and rely on the default limiter
2026-05-01 17:53:49 +00:00
'instagram' => [
'settings' => 'Instagram Settings' ,
'posting_to' => 'Posting to' ,
'variant_label' => 'Post type' ,
'variant' => [
'feed' => 'Feed Post' ,
'reel' => 'Reel' ,
'story' => 'Story' ,
],
2026-05-02 15:22:42 +00:00
'aspect_label' => 'Aspect ratio' ,
'aspect' => [
'square' => 'Square (1:1)' ,
'portrait' => 'Portrait (4:5)' ,
'landscape' => 'Landscape (16:9)' ,
'original' => 'Original' ,
],
feat: media gallery picker, custom emoji picker, preview tabs, real platform logos
- gallery: extract /assets tabs (uploads, Unsplash, Giphy) into shared
GalleryBrowser used by both /assets and a new MediaPickerDialog inside the
post editor; add JSON search endpoint for workspace assets with tests
- emoji: replace broken emoji-picker-element web component with a custom
EmojiPicker (full Unicode set, search, categories, recently-used,
light/dark, i18n)
- preview tab: platform selector pills, variant tabs (data-driven from
content_types map) so the user can switch Feed/Reel/Story etc. and have
it autosave through the same handler ScheduleTab uses
- platform logos: shared usePlatformLogo composable (logo + label + content
types); replaces inline maps across 5 components, fixes
instagram-facebook falling back to default.png
- tooltips: hover details (display_name · @username + platform label) on
platform avatars across editor, posts list and calendar
- settings cards: show ` · @username` in the title bar so multiple accounts
on the same network are distinguishable
- routes: drop the throttle:6,1 group middleware on social connect routes
(was 429ing legitimate OAuth retries) and rely on the default limiter
2026-05-01 17:53:49 +00:00
],
'facebook' => [
'settings' => 'Facebook Settings' ,
'posting_to' => 'Posting to' ,
'variant_label' => 'Post type' ,
'variant' => [
'post' => 'Post' ,
'reel' => 'Reel' ,
'story' => 'Story' ,
],
],
2026-05-02 17:50:36 +00:00
'linkedin' => [
'settings' => 'LinkedIn Settings' ,
'settings_page' => 'LinkedIn Page Settings' ,
'posting_to' => 'Posting to' ,
'variant_label' => 'Post type' ,
'variant' => [
'post' => 'Post' ,
'carousel' => 'Carousel' ,
],
],
'pinterest' => [
'settings' => 'Pinterest Settings' ,
'posting_to' => 'Posting to' ,
'variant_label' => 'Pin type' ,
'variant' => [
'pin' => 'Pin' ,
'video_pin' => 'Video Pin' ,
'carousel' => 'Carousel' ,
],
2026-05-15 14:51:20 +00:00
'board' => 'Board' ,
'select_board' => 'Select a board' ,
'no_boards' => 'No Pinterest boards found. Create one in your Pinterest account first.' ,
'search_board' => 'Search boards...' ,
'no_board_found' => 'No board matches your search.' ,
'board_required' => 'Select a Pinterest board to publish this post.' ,
2026-05-02 17:50:36 +00:00
],
feat: media gallery picker, custom emoji picker, preview tabs, real platform logos
- gallery: extract /assets tabs (uploads, Unsplash, Giphy) into shared
GalleryBrowser used by both /assets and a new MediaPickerDialog inside the
post editor; add JSON search endpoint for workspace assets with tests
- emoji: replace broken emoji-picker-element web component with a custom
EmojiPicker (full Unicode set, search, categories, recently-used,
light/dark, i18n)
- preview tab: platform selector pills, variant tabs (data-driven from
content_types map) so the user can switch Feed/Reel/Story etc. and have
it autosave through the same handler ScheduleTab uses
- platform logos: shared usePlatformLogo composable (logo + label + content
types); replaces inline maps across 5 components, fixes
instagram-facebook falling back to default.png
- tooltips: hover details (display_name · @username + platform label) on
platform avatars across editor, posts list and calendar
- settings cards: show ` · @username` in the title bar so multiple accounts
on the same network are distinguishable
- routes: drop the throttle:6,1 group middleware on social connect routes
(was 429ing legitimate OAuth retries) and rely on the default limiter
2026-05-01 17:53:49 +00:00
'warnings' => [
'no_variant' => 'Pick a post type to continue.' ,
'requires_media' => 'This post type requires at least one image or video.' ,
'max_files_exceeded' => 'This post type accepts up to :max media files (you have :current).' ,
'min_files_required' => 'This post type requires at least :min media files (you have :current).' ,
'no_video_allowed' => 'This post type does not accept videos.' ,
'no_image_allowed' => 'This post type accepts only videos.' ,
'gif_not_allowed' => 'This platform does not accept GIF. Remove the GIF or choose a different network.' ,
'image_too_large' => 'Image exceeds the :max limit for this post type (yours is :current).' ,
'video_too_large' => 'Video exceeds the :max limit for this post type (yours is :current).' ,
'video_too_long' => 'Video is :current long, but this post type allows up to :max.' ,
'aspect_ratio_too_narrow' => 'Aspect ratio :current is too tall for this post type (min :min).' ,
'aspect_ratio_too_wide' => 'Aspect ratio :current is too wide for this post type (max :max).' ,
],
2026-01-22 00:46:27 +00:00
],
2026-01-21 23:58:08 +00:00
'status' => [
2026-03-30 19:55:14 +00:00
'pending' => 'Pending' ,
2026-01-21 23:58:08 +00:00
'draft' => 'Draft' ,
'scheduled' => 'Scheduled' ,
'publishing' => 'Publishing' ,
2026-05-19 13:03:30 +00:00
'retrying' => 'Retrying' ,
2026-01-21 23:58:08 +00:00
'published' => 'Published' ,
'partially_published' => 'Partially Published' ,
'failed' => 'Failed' ,
],
'descriptions' => [
'draft' => 'Posts waiting to be scheduled' ,
'scheduled' => 'Posts scheduled for publishing' ,
'published' => 'Posts already published' ,
],
2026-01-22 00:33:31 +00:00
2026-05-03 12:36:50 +00:00
'ai' => [
'generate' => [
'button_tooltip' => 'Generate with AI' ,
'title' => 'Generate post with AI' ,
'description' => 'Describe what the post should be about. The AI will use your brand context to write it.' ,
'prompt_label' => 'What is this post about?' ,
'prompt_placeholder' => 'e.g. Announce our new image-generation feature for carousels' ,
'preview_label' => 'Preview' ,
'start' => 'Generate' ,
'apply' => 'Use this content' ,
'retry' => 'Try again' ,
'cancel' => 'Cancel' ,
],
'review' => [
'button_tooltip' => 'Review with AI' ,
'title' => 'Review post with AI' ,
2026-05-03 16:44:13 +00:00
'description' => 'AI scans for grammar, spelling, and clarity. Apply the suggestions you agree with.' ,
2026-05-03 12:36:50 +00:00
'loading' => 'Reviewing your text...' ,
'no_issues' => 'No issues found. Looks good.' ,
'original' => 'Original' ,
'suggestion' => 'Suggestion' ,
'apply' => 'Apply' ,
2026-05-03 16:44:13 +00:00
'apply_all' => 'Apply all' ,
2026-05-03 12:36:50 +00:00
'applied' => 'Applied' ,
2026-05-03 16:44:13 +00:00
'cancel' => 'Cancel' ,
2026-05-03 12:36:50 +00:00
],
2026-05-21 19:27:55 +00:00
'image_regenerate' => [
'button' => 'Adjust' ,
'title' => 'Adjust AI image' ,
'description' => 'Describe the correction. The new image replaces the current one and keeps its position in the carousel.' ,
'instruction_label' => 'Instruction' ,
'instruction_placeholder' => 'e.g. Fix the typo in the headline and make the background lighter.' ,
'processing' => 'Regenerating image... this can take a few seconds.' ,
'submit' => 'Regenerate image' ,
'cancel' => 'Cancel' ,
'success' => 'Image updated. The new version replaced the previous one in your post.' ,
'fallback_title' => 'Improve this image copy' ,
'errors' => [
'required' => 'Instruction is required.' ,
'start_failed' => 'Failed to start regeneration.' ,
'unavailable' => 'Unable to regenerate this image right now.' ,
'timeout' => 'Regeneration is taking longer than expected. Try again in a moment.' ,
'media_not_found' => 'Media item not found.' ,
'not_ai_media' => 'Only AI-generated media can be regenerated.' ,
],
],
2026-05-03 12:36:50 +00:00
],
2026-05-02 15:22:42 +00:00
'show' => [
'title' => 'Post Details' ,
'edit' => 'Edit' ,
refactor: in-page page headers, indies tabs, and post details polish
- Replace global AppHeader slots with in-page headers on Calendar, Edit,
and Show pages — full-width canvas with the page-specific controls
living in their own ink-bordered bar.
- Remove sidebar collapse mechanism: drop Cmd+B keyboard shortcut, force
defaultOpen=true (ignore persisted cookie), make toggleSidebar a no-op
on desktop. Sidebar is permanent on desktop, sheet behavior preserved
on mobile.
- Redesign Tabs to match the design system: each TabsTrigger renders as
a standalone sticker button (border-2 + rounded-md + shadow-xs), with
amber-200 active state mirroring the "POST" badge from post-templates.
- Show page layout: cap preview Card to max-w-xl, preserve natural
aspect for single-image posts (object-contain, max-h-[480px]), drop
redundant stats grid, swap header sides (Back button left, status +
date right with date first).
- Assets: bump GalleryBrowser search inputs to h-12 for comfortable
browsing across uploads, Unsplash, and Giphy tabs.
- Fix PostPlatformMetrics loading-forever bug: hoist useHttp out of the
onMounted callback into setup, and read the response array directly
(backend returns the array at root, not under .data).
- i18n: rename posts.show.back_to_posts → posts.show.back; remove unused
posts.show.summary.* keys.
2026-05-06 19:22:55 +00:00
'back' => 'Back' ,
2026-05-02 15:22:42 +00:00
'no_content' => 'No caption' ,
'platforms' => 'Platforms' ,
'no_platforms' => 'No platforms selected.' ,
'view_on_platform' => 'View on platform' ,
'published_on' => 'Published on :date' ,
'scheduled_for' => 'Scheduled for :date' ,
'draft' => 'Draft' ,
'status_pending' => 'Pending' ,
'metrics' => 'Metrics' ,
'metrics_loading' => 'Loading metrics…' ,
'metrics_unavailable' => 'Metrics unavailable for this platform yet.' ,
'metrics_empty' => 'No metrics returned.' ,
],
2026-01-22 00:33:31 +00:00
'edit' => [
'title' => 'Edit Post' ,
'view_title' => 'View Post' ,
2026-01-26 18:18:35 +00:00
'labels' => 'Labels' ,
2026-04-15 23:11:36 +00:00
'no_labels' => 'No labels created yet' ,
2026-01-22 00:33:31 +00:00
'schedule' => 'Schedule' ,
2026-04-15 23:11:36 +00:00
'pick_time' => 'Pick time' ,
2026-05-19 19:26:40 +00:00
'pick_time_past' => 'Pick a future date and time.' ,
2026-04-15 23:11:36 +00:00
'post_now' => 'Post now' ,
2026-04-17 02:05:51 +00:00
'time' => 'Time' ,
'cancel' => 'Cancel' ,
2026-01-26 19:16:34 +00:00
'delete' => 'Delete' ,
'schedule_for' => 'Schedule for' ,
2026-04-15 23:11:36 +00:00
'schedule_date' => 'Schedule date' ,
2026-05-06 17:26:18 +00:00
'unschedule' => 'Unschedule' ,
2026-01-22 00:33:31 +00:00
'saving' => 'Saving...' ,
'saved' => 'Saved' ,
2026-04-17 02:05:51 +00:00
'draft' => 'Draft' ,
2026-01-22 00:33:31 +00:00
'media' => 'Media' ,
2026-04-17 02:05:51 +00:00
'add_media' => 'Add media' ,
2026-01-22 00:33:31 +00:00
'caption' => 'Caption' ,
2026-04-15 23:11:36 +00:00
'caption_placeholder' => 'Write your caption...' ,
2026-04-17 02:05:51 +00:00
'compose_title' => 'Create a post' ,
'compose_subtitle' => 'Compose your message and add media' ,
2026-05-02 17:50:36 +00:00
'preview_empty' => [
'title' => 'No platform selected' ,
'description' => 'Select a platform to publish to see the preview.' ,
],
2026-05-02 18:32:55 +00:00
'drop_zone_title' => 'Add media' ,
'drop_zone_subtitle' => 'Drag & drop files or click to browse' ,
'add' => 'Add' ,
2026-04-15 23:11:36 +00:00
'publish_to' => 'Publish to' ,
'organize' => 'Organize' ,
2026-05-03 18:23:30 +00:00
'signatures' => 'Signatures' ,
2026-04-15 23:11:36 +00:00
'view_on_platform' => 'View on platform' ,
'platform_status' => 'Platform status' ,
feat: media gallery picker, custom emoji picker, preview tabs, real platform logos
- gallery: extract /assets tabs (uploads, Unsplash, Giphy) into shared
GalleryBrowser used by both /assets and a new MediaPickerDialog inside the
post editor; add JSON search endpoint for workspace assets with tests
- emoji: replace broken emoji-picker-element web component with a custom
EmojiPicker (full Unicode set, search, categories, recently-used,
light/dark, i18n)
- preview tab: platform selector pills, variant tabs (data-driven from
content_types map) so the user can switch Feed/Reel/Story etc. and have
it autosave through the same handler ScheduleTab uses
- platform logos: shared usePlatformLogo composable (logo + label + content
types); replaces inline maps across 5 components, fixes
instagram-facebook falling back to default.png
- tooltips: hover details (display_name · @username + platform label) on
platform avatars across editor, posts list and calendar
- settings cards: show ` · @username` in the title bar so multiple accounts
on the same network are distinguishable
- routes: drop the throttle:6,1 group middleware on social connect routes
(was 429ing legitimate OAuth retries) and rely on the default limiter
2026-05-01 17:53:49 +00:00
'compliance_incomplete' => 'Some platform settings are incomplete or incompatible with the attached media.' ,
2026-05-02 15:22:42 +00:00
'compliance' => [
2026-05-19 17:18:13 +00:00
'requires_content_or_media' => 'Add text or media to publish.' ,
2026-05-02 15:22:42 +00:00
'requires_media' => 'Add an image or video to publish here.' ,
'too_many_files' => 'Only :max file(s) allowed for this format.' ,
'too_few_files' => 'Add at least :min files for this format.' ,
'no_videos' => 'Only images are allowed for this format.' ,
'no_images' => 'Only videos are allowed for this format.' ,
'no_gifs' => 'GIFs are not supported here.' ,
'video_too_large' => 'Video exceeds the size limit for this platform.' ,
'video_too_long' => 'Video must be under :seconds seconds for this format.' ,
'image_too_large' => 'Image exceeds the size limit for this platform.' ,
'aspect_ratio_invalid' => 'Aspect ratio is not supported by this format.' ,
'no_content_type' => 'Pick a content type for this platform.' ,
],
feat: media gallery picker, custom emoji picker, preview tabs, real platform logos
- gallery: extract /assets tabs (uploads, Unsplash, Giphy) into shared
GalleryBrowser used by both /assets and a new MediaPickerDialog inside the
post editor; add JSON search endpoint for workspace assets with tests
- emoji: replace broken emoji-picker-element web component with a custom
EmojiPicker (full Unicode set, search, categories, recently-used,
light/dark, i18n)
- preview tab: platform selector pills, variant tabs (data-driven from
content_types map) so the user can switch Feed/Reel/Story etc. and have
it autosave through the same handler ScheduleTab uses
- platform logos: shared usePlatformLogo composable (logo + label + content
types); replaces inline maps across 5 components, fixes
instagram-facebook falling back to default.png
- tooltips: hover details (display_name · @username + platform label) on
platform avatars across editor, posts list and calendar
- settings cards: show ` · @username` in the title bar so multiple accounts
on the same network are distinguishable
- routes: drop the throttle:6,1 group middleware on social connect routes
(was 429ing legitimate OAuth retries) and rely on the default limiter
2026-05-01 17:53:49 +00:00
'publishing' => 'Publishing...' ,
'publishing_overlay_title' => 'Your post is being published' ,
'publishing_overlay_subtitle' => 'This can take a few moments. You can safely leave this page.' ,
2026-05-11 23:47:53 +00:00
'scheduled_overlay_title' => 'This post is scheduled' ,
'scheduled_overlay_subtitle' => 'Scheduled for :date. Unschedule it first to make changes.' ,
'unschedule_cta' => 'Unschedule to edit' ,
2026-04-15 23:11:36 +00:00
'tabs' => [
'preview' => 'Preview' ,
'schedule' => 'Schedule' ,
'comments' => 'Comments' ,
'comments_empty' => 'No comments yet.' ,
],
2026-01-22 00:33:31 +00:00
feat: media gallery picker, custom emoji picker, preview tabs, real platform logos
- gallery: extract /assets tabs (uploads, Unsplash, Giphy) into shared
GalleryBrowser used by both /assets and a new MediaPickerDialog inside the
post editor; add JSON search endpoint for workspace assets with tests
- emoji: replace broken emoji-picker-element web component with a custom
EmojiPicker (full Unicode set, search, categories, recently-used,
light/dark, i18n)
- preview tab: platform selector pills, variant tabs (data-driven from
content_types map) so the user can switch Feed/Reel/Story etc. and have
it autosave through the same handler ScheduleTab uses
- platform logos: shared usePlatformLogo composable (logo + label + content
types); replaces inline maps across 5 components, fixes
instagram-facebook falling back to default.png
- tooltips: hover details (display_name · @username + platform label) on
platform avatars across editor, posts list and calendar
- settings cards: show ` · @username` in the title bar so multiple accounts
on the same network are distinguishable
- routes: drop the throttle:6,1 group middleware on social connect routes
(was 429ing legitimate OAuth retries) and rely on the default limiter
2026-05-01 17:53:49 +00:00
'media_picker' => [
'title' => 'Pick from gallery' ,
'search' => 'Search media...' ,
'empty' => 'No media in your gallery yet' ,
'cancel' => 'Cancel' ,
'add' => 'Add' ,
'add_count' => 'Add :count' ,
],
'emoji_picker' => [
'search' => 'Search emoji' ,
'empty' => 'No emojis found' ,
'recent' => 'Frequently used' ,
'smileys' => 'Smileys & emotion' ,
'people' => 'People & body' ,
'nature' => 'Animals & nature' ,
'food' => 'Food & drink' ,
'activities' => 'Activities' ,
'travel' => 'Travel & places' ,
'objects' => 'Objects' ,
'symbols' => 'Symbols' ,
'flags' => 'Flags' ,
],
2026-04-15 23:11:36 +00:00
'status' => [
2026-05-06 17:26:18 +00:00
'scheduled' => 'Scheduled' ,
2026-04-15 23:11:36 +00:00
'published' => 'Published' ,
'publishing' => 'Publishing...' ,
2026-05-19 13:03:30 +00:00
'retrying' => 'Retrying...' ,
2026-04-15 23:11:36 +00:00
'failed' => 'Failed' ,
2026-01-22 00:33:31 +00:00
],
'delete_modal' => [
'title' => 'Delete Post' ,
'description' => 'Are you sure you want to delete this post? This action cannot be undone.' ,
'action' => 'Delete' ,
'cancel' => 'Cancel' ,
],
'sync_enable' => [
2026-01-22 00:46:27 +00:00
'title' => 'Enable sync?' ,
'description' => 'All platforms will share the same content. Any custom edits made to individual platforms will be replaced with the current content.' ,
2026-01-22 00:33:31 +00:00
'cancel' => 'Cancel' ,
2026-01-22 00:46:27 +00:00
'action' => 'Enable sync' ,
2026-01-22 00:33:31 +00:00
],
'sync_disable' => [
'title' => 'Disable sync?' ,
'description' => 'Each platform will keep its current content, but future edits will only apply to the platform you\'re editing.' ,
'customize_note' => 'You\'ll be able to customize the content for each platform individually.' ,
'cancel' => 'Cancel' ,
'action' => 'Disable sync' ,
],
'platforms_dialog' => [
'title' => 'Select Platforms' ,
'description' => 'Choose which platforms to publish this post to.' ,
],
2026-05-03 18:23:30 +00:00
'signatures_modal' => [
'search' => 'Search signatures...' ,
'no_results' => 'No signatures found.' ,
2026-01-26 18:18:35 +00:00
],
2026-01-22 00:33:31 +00:00
'validation' => [
'select_board' => 'Select a board' ,
'images_not_supported' => 'Images not supported' ,
'videos_not_supported' => 'Videos not supported' ,
'max_images' => 'Max :count images' ,
'requires_media' => 'Requires media' ,
2026-04-01 18:12:56 +00:00
'requires_content' => 'Text content is required' ,
2026-01-22 00:33:31 +00:00
'exceeded' => ':count exceeded' ,
'does_not_support_images' => ':platform does not support images' ,
'supports_up_to_images' => ':platform supports up to :count images' ,
'does_not_support_videos' => ':platform does not support videos' ,
],
],
'content_types' => [
'instagram_feed' => [
'label' => 'Feed Post' ,
'description' => 'Appears in your feed and profile' ,
],
'instagram_reel' => [
'label' => 'Reel' ,
'description' => 'Short video up to 90 seconds' ,
],
'instagram_story' => [
'label' => 'Story' ,
'description' => 'Disappears after 24 hours' ,
],
'linkedin_post' => [
'label' => 'Post' ,
'description' => 'Standard post with text and media' ,
],
'linkedin_carousel' => [
'label' => 'Carousel' ,
'description' => 'Swipeable images' ,
],
'linkedin_page_post' => [
'label' => 'Post' ,
'description' => 'Standard post with text and media' ,
],
'linkedin_page_carousel' => [
'label' => 'Carousel' ,
'description' => 'Swipeable images' ,
],
'facebook_post' => [
'label' => 'Post' ,
'description' => 'Standard post on your page' ,
],
'facebook_reel' => [
'label' => 'Reel' ,
'description' => 'Short video up to 90 seconds' ,
],
'facebook_story' => [
'label' => 'Story' ,
'description' => 'Disappears after 24 hours' ,
],
'tiktok_video' => [
'label' => 'Video' ,
'description' => 'Short-form video content' ,
],
2026-05-09 16:56:00 +00:00
'tiktok_photo' => [
'label' => 'Photo carousel' ,
'description' => 'Up to 35 photos as a swipeable carousel' ,
],
2026-01-22 00:33:31 +00:00
'youtube_short' => [
'label' => 'Short' ,
'description' => 'Vertical video up to 60 seconds' ,
],
'x_post' => [
'label' => 'Post' ,
'description' => 'Tweet with text and media' ,
],
'threads_post' => [
'label' => 'Post' ,
'description' => 'Text post with optional media' ,
],
'pinterest_pin' => [
'label' => 'Pin' ,
2026-05-09 16:56:00 +00:00
'description' => 'Standard image pin' ,
2026-01-22 00:33:31 +00:00
],
'pinterest_video_pin' => [
'label' => 'Video Pin' ,
2026-05-09 16:56:00 +00:00
'description' => 'Video pin (4s - 15min)' ,
2026-01-22 00:33:31 +00:00
],
'pinterest_carousel' => [
'label' => 'Carousel' ,
2026-05-09 16:56:00 +00:00
'description' => 'Multi-image carousel (2-5 images)' ,
2026-01-22 00:33:31 +00:00
],
'bluesky_post' => [
'label' => 'Post' ,
'description' => 'Text post with optional images' ,
],
'mastodon_post' => [
'label' => 'Post' ,
'description' => 'Text post with optional media' ,
],
],
'platforms' => [
'linkedin' => 'LinkedIn' ,
'linkedin-page' => 'LinkedIn Page' ,
'x' => 'X' ,
'tiktok' => 'TikTok' ,
'youtube' => 'YouTube Shorts' ,
'facebook' => 'Facebook Page' ,
'instagram' => 'Instagram' ,
'threads' => 'Threads' ,
'pinterest' => 'Pinterest' ,
'bluesky' => 'Bluesky' ,
'mastodon' => 'Mastodon' ,
],
2026-01-22 01:08:18 +00:00
'flash' => [
'scheduled' => 'Post scheduled successfully!' ,
'deleted' => 'Post deleted successfully!' ,
2026-05-04 01:37:51 +00:00
'duplicated' => 'Post duplicated as a draft.' ,
fix(facebook): empty-message rejection + state consistency + no re-publish on terminal
Production incident: a customer's Facebook Page post failed with 'The post
is empty. Please enter a message to share.' (error code 197) and ended up
with a contradictory DB state (status=published + error_message=set).
Three independent bugs were uncovered:
A. FacebookPublisher sends 'message'/'description' as null when the user
posts media without text. Graph API requires the key be omitted, not
null. Fixed in publishSingleImagePost, publishMultiImagePost,
publishVideoPost, publishReel.
B. markAsPublished/markAsFailed leak stale fields across transitions
(a published row could retain error_message from a prior failure,
vice-versa). Both transitions now explicitly clear the opposite
side's fields.
C. status='failed' was editable in the UI and the backend, so users
were re-clicking Publish, generating duplicate failure emails and
the contradictory state from bug B. The frontend isReadOnly check
and the UpdatePost backend guard now treat Published/PartiallyPublished/
Failed/Publishing as terminal. To retry, the user duplicates the post.
11 new tests guarantee these can't regress silently: FB payload shape
per content type, PostPlatform field-clearing on transitions, and the
terminal-status block at the controller level.
2026-05-15 16:01:49 +00:00
'cannot_edit_finalized' => 'This post has already been processed and cannot be re-published. Duplicate it to try again.' ,
2026-05-02 15:22:42 +00:00
'cannot_delete_published' => 'Published posts cannot be deleted.' ,
2026-01-22 01:08:18 +00:00
'connect_first' => 'Connect at least one social network before creating a post.' ,
],
2026-01-26 13:54:29 +00:00
'errors' => [
'account_disconnected' => 'Social account is disconnected' ,
2026-03-31 00:18:07 +00:00
'account_inactive' => 'Social account is deactivated' ,
2026-05-12 21:46:06 +00:00
'account_token_expired' => 'Social account session expired — please reconnect' ,
2026-01-26 13:54:29 +00:00
],
2026-05-03 12:36:50 +00:00
2026-05-03 13:17:01 +00:00
'delete' => [
'title' => 'Delete post?' ,
'description' => 'This action can\'t be undone. The post and all its media will be permanently removed.' ,
'confirm' => 'Yes, delete' ,
'cancel' => 'Cancel' ,
],
2026-05-03 12:36:50 +00:00
'create' => [
'title' => 'Create a new post' ,
'description' => 'Choose how you want to start.' ,
'scratch_title' => 'Start from scratch' ,
'scratch_description' => 'Open a blank post and write everything yourself.' ,
'ai_title' => 'Generate with AI' ,
'ai_description' => 'Describe what you want and AI generates the content for you.' ,
'ai_configure_description' => 'Pick a format and describe the post you want to create.' ,
'template_title' => 'Use a template' ,
'template_description' => 'Pick from our curated templates and customize.' ,
'coming_soon' => 'Coming soon' ,
2026-05-03 13:06:02 +00:00
'preview' => [
'image_title' => 'Image title' ,
'image_body' => 'Image body' ,
],
2026-05-03 12:36:50 +00:00
'steps' => [
'format_title' => 'Choose a format' ,
'format_description' => 'Select the type of post you want to create.' ,
'account_title' => 'Choose an account' ,
'account_description' => 'Select the social account to publish to.' ,
'media_title' => 'Media options' ,
'media_carousel' => 'How many slides?' ,
'media_optional' => 'Include images?' ,
'media_optional_label' => 'How many images?' ,
'media_none' => 'None' ,
'media_count_label' => 'Number of images' ,
'prompt_title' => 'Describe your post' ,
'prompt_label' => 'What is this post about?' ,
'prompt_placeholder' => 'e.g. Announce our new carousel feature for Instagram' ,
'preview_error' => 'Something went wrong. Please try again.' ,
2026-05-08 21:30:11 +00:00
'loading_page_title' => 'Generating your post' ,
'loading_eta' => 'Estimated time: about :minutes.' ,
'loading_eta_minute_one' => '1 minute' ,
'loading_eta_minute_other' => ':count minutes' ,
'loading_leave_title' => 'You can keep working.' ,
'loading_leave_body' => 'We will notify you when the post is ready.' ,
'loading_leave_cta' => 'Go to calendar' ,
'loading_create_another_cta' => 'Create another post' ,
'loading_tip_credits' => 'Each AI image uses about 15 credits.' ,
'loading_tip_edit' => 'You will be able to edit everything once the post is ready.' ,
'loading_tip_draft' => 'Generated posts land in your drafts.' ,
'loading_tip_brand' => 'Tweak your brand settings to influence future posts.' ,
'loading_tip_carousel' => 'Carousels deliver one slide per uploaded image.' ,
'loading_tip_quality' => 'Image quality is set to balance speed and cost.' ,
2026-05-03 12:36:50 +00:00
'create' => 'Create post' ,
'back' => 'Back' ,
'next' => 'Continue' ,
'cancel' => 'Cancel' ,
'discard' => 'Discard' ,
'retry' => 'Try again' ,
'no_platforms' => 'No connected accounts' ,
'connect_first' => 'Connect at least one social account to use AI generation.' ,
'format' => [
'instagram_feed' => 'Instagram Feed Post' ,
'instagram_carousel' => 'Instagram Carousel' ,
'linkedin_post' => 'LinkedIn Post' ,
'linkedin_page_post' => 'LinkedIn Page Post' ,
'x_post' => 'X Post' ,
'bluesky_post' => 'Bluesky Post' ,
'threads_post' => 'Threads Post' ,
'mastodon_post' => 'Mastodon Post' ,
'facebook_post' => 'Facebook Post' ,
'pinterest_pin' => 'Pinterest Pin' ,
'instagram_story' => 'Instagram Story' ,
'facebook_story' => 'Facebook Story' ,
],
],
],
'templates' => [
'browser_title' => 'Choose a template' ,
'browser_description' => 'Start from a curated template and adapt it.' ,
'search_placeholder' => 'Search templates…' ,
'no_search_results' => 'No templates match your search' ,
'try_different_search' => 'Try a different keyword or clear the search.' ,
'slides_count' => '{count} slide|{count} slides' ,
'all_platforms' => 'All platforms' ,
2026-05-06 17:26:18 +00:00
'platform_search_placeholder' => 'Search platform…' ,
'no_platform_match' => 'No platform matches.' ,
2026-05-03 12:36:50 +00:00
'use_this' => 'Use this template' ,
'no_templates' => 'No templates available.' ,
'applying' => 'Applying template…' ,
'category' => [
'product_launch' => 'Product launch' ,
'promotion' => 'Promotion' ,
'educational' => 'Educational' ,
'behind_the_scenes' => 'Behind the scenes' ,
'testimonial' => 'Testimonial' ,
'industry_tip' => 'Industry tip' ,
'event' => 'Event' ,
'engagement' => 'Engagement' ,
],
],
2026-01-21 23:58:08 +00:00
];