trypost/lang/pt-BR/posts.php

671 lines
31 KiB
PHP
Raw Permalink Normal View History

2026-01-26 13:55:27 +00:00
<?php
return [
'title' => 'Posts',
2026-03-31 00:18:07 +00:00
'search' => 'Buscar posts...',
2026-01-26 13:55:27 +00:00
'all_posts' => 'Todos os Posts',
'new_post' => 'Novo Post',
'no_posts' => 'Nenhum post encontrado',
'no_search_results' => 'Nenhum post corresponde à sua busca',
'try_different_search' => 'Tente outra palavra-chave ou limpe a busca.',
2026-01-26 13:55:27 +00:00
'start_creating' => 'Comece criando seu primeiro post.',
'filter_by_label' => 'Filtrar por label',
'label_search_placeholder' => 'Buscar labels...',
'no_labels' => 'Nenhuma label encontrada.',
'clear_label_filter' => 'Limpar filtro de labels',
'table' => [
'post' => 'Post',
'status' => 'Status',
'content' => 'Conteúdo',
'platforms' => 'Plataformas',
'labels' => 'Etiquetas',
'scheduled_at' => 'Data',
'actions' => '',
],
2026-01-26 13:55:27 +00:00
'manage_posts' => 'Gerencie todos os seus posts',
'delete_confirm' => 'Tem certeza que deseja excluir este post?',
'by' => 'por',
'actions' => [
'view' => 'Ver post',
'delete' => 'Excluir',
'duplicate' => 'Duplicar',
'copy_id' => 'Copiar ID',
'copied' => 'ID copiado para a área de transferência',
2026-01-26 13:55:27 +00:00
],
'form' => [
'post_type' => 'Tipo de Post',
'board' => 'Pasta',
'select_board' => 'Selecione uma pasta',
'search_board' => 'Buscar pasta...',
'no_board_found' => 'Nenhuma pasta encontrada',
'media' => 'Mídia',
'min' => 'Mín',
'uploading' => 'Enviando...',
'drop_to_upload' => 'Solte para enviar',
'drag_and_drop' => 'Arraste e solte ou clique para enviar',
'photos_and_videos' => 'Fotos e vídeos',
'photos_only' => 'Apenas fotos',
'videos_only' => 'Apenas vídeos',
'drag_to_reorder' => 'Arraste para reordenar',
'caption' => 'Legenda',
'write_caption' => 'Escreva sua legenda...',
'content_exceeds_platform' => ':platform: longo demais por :over caracteres (máx :limit).',
2026-04-02 20:57:06 +00:00
'tiktok' => [
'settings' => 'Configurações do TikTok',
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' => 'Tipo de publicação',
'variant' => [
'video' => 'Vídeo',
'photo' => 'Carrossel de fotos',
],
2026-04-23 16:23:24 +00:00
'posting_to' => 'Publicando em',
2026-04-02 20:57:06 +00:00
'privacy_level' => 'Quem pode ver este vídeo?',
'privacy_placeholder' => 'Selecione quem pode ver este post',
2026-04-02 20:57:06 +00:00
'privacy' => [
'public' => 'Público para todos',
'friends' => 'Amigos em comum',
'followers' => 'Seguidores',
'private' => 'Apenas eu',
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' => 'Conteúdo de marca não pode ser privado.',
2026-04-02 20:57:06 +00:00
],
'privacy_hint' => 'As opções disponíveis dependem das configurações da sua conta TikTok.',
'auto_add_music' => 'Adicionar música automaticamente',
'auto_add_music_hint' => 'Disponível apenas para fotos. Adiciona uma música padrão que pode ser alterada depois.',
'yes' => 'Sim',
'no' => 'Não',
'allow_users' => 'Permitir que usuários:',
'comments' => 'Comentem',
'duet' => 'Dueto',
'stitch' => 'Stitch',
'is_aigc' => 'Vídeo feito com IA',
2026-04-23 16:23:24 +00:00
'disclose' => 'Divulgar conteúdo do vídeo',
'disclose_hint' => 'Ative para divulgar que este vídeo promove bens ou serviços em troca de algo de valor. Seu vídeo pode promover você, terceiros ou ambos.',
'promotional_organic_title' => 'Sua foto/vídeo será rotulado como "Conteúdo Promocional".',
'promotional_paid_title' => 'Sua foto/vídeo será rotulado como "Parceria paga".',
'promotional_description' => 'Isso não poderá ser alterado após a publicação.',
'compliance_incomplete' => 'Você precisa indicar se o conteúdo promove você mesmo, terceiros ou ambos.',
'privacy_required' => 'A visibilidade do TikTok é obrigatória ao publicar.',
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' => 'A visibilidade foi limpa porque conteúdo de marca não pode ser privado.',
'interaction_disabled_by_creator' => 'Desativado nas configurações da sua conta TikTok',
2026-04-23 16:23:24 +00:00
'max_duration_exceeded' => 'Vídeo tem :duration s mas esta conta só permite vídeos de até :max s.',
'processing_hint' => 'Após publicar, pode levar alguns minutos para o conteúdo ser processado e aparecer no seu perfil TikTok.',
'brand_organic' => 'Sua marca',
'brand_organic_hint' => 'Você está promovendo você mesmo ou sua própria marca. Este vídeo será classificado como Brand Organic.',
'brand_content' => 'Conteúdo patrocinado',
'brand_content_hint' => 'Você está promovendo outra marca ou terceiros. Este vídeo será classificado como Branded Content.',
'compliance' => [
'agree' => 'Ao publicar, você concorda com a',
'music_usage' => 'Confirmação de Uso de Música',
'and' => 'e',
'branded_policy' => 'Política de Conteúdo Patrocinado',
],
2026-04-02 20:57:06 +00:00
],
'instagram' => [
'settings' => 'Configurações do Instagram',
'posting_to' => 'Publicando em',
'variant_label' => 'Tipo de publicação',
'variant' => [
'feed' => 'Post',
'reel' => 'Reel',
'story' => 'Story',
],
'aspect_label' => 'Proporção',
'aspect' => [
'square' => 'Quadrado (1:1)',
'portrait' => 'Retrato (4:5)',
'landscape' => 'Paisagem (16:9)',
'original' => 'Original',
],
],
'facebook' => [
'settings' => 'Configurações do Facebook',
'posting_to' => 'Publicando em',
'variant_label' => 'Tipo de publicação',
'variant' => [
'post' => 'Post',
'reel' => 'Reel',
'story' => 'Story',
],
'aspect_label' => 'Proporção',
'aspect' => [
'square' => 'Quadrado (1:1)',
'portrait' => 'Retrato (4:5)',
'landscape' => 'Paisagem (16:9)',
'original' => 'Original',
],
],
'linkedin' => [
'settings' => 'Configurações do LinkedIn',
'settings_page' => 'Configurações da Página do LinkedIn',
'posting_to' => 'Publicando em',
'document_title' => 'Título do documento',
'document_title_placeholder' => 'Aparece no seu post de documento PDF',
],
'pinterest' => [
'settings' => 'Configurações do Pinterest',
'posting_to' => 'Publicando em',
'variant_label' => 'Tipo de pin',
'variant' => [
'pin' => 'Pin',
'video_pin' => 'Video Pin',
'carousel' => 'Carrossel',
],
'board' => 'Quadro',
'select_board' => 'Selecione um quadro',
'no_boards' => 'Nenhum quadro do Pinterest encontrado. Crie um na sua conta do Pinterest primeiro.',
'boards_truncated' => 'Alguns boards não puderam ser carregados. Se o seu não aparecer, abra o Pinterest e tente de novo.',
'search_board' => 'Pesquisar quadros...',
'no_board_found' => 'Nenhum quadro encontrado.',
'board_required' => 'Selecione um quadro do Pinterest para publicar este post.',
Add optional Pinterest pin title and destination link (#232) * Add optional Pinterest pin title, description, and link. Expose title/description/link across web, API, and MCP; seed description from caption into meta on save, and publish description only from meta. Co-authored-by: Cursor <cursoragent@cursor.com> * Expand Pinterest title/description/link test coverage. Cover web draft persistence and validation bounds, API/MCP update merge and seed, and publisher payload fields on video and carousel pins. Co-authored-by: Cursor <cursoragent@cursor.com> * Simplify Pinterest: description is post content again. Keep optional title and link in meta/settings only. Remove the separate description textarea, seed logic, and meta.description path so Pinterest follows the shared caption pattern. Co-authored-by: Cursor <cursoragent@cursor.com> * Refactor Pinterest meta handling and validation. - Update CreatePost and UpdatePost actions to filter out null values from meta fields. - Introduce a new method in PinterestPublisher to resolve board IDs, ensuring required fields are validated. - Enhance PinterestSettings component to manage title and link inputs, including validation for HTTP URLs. - Update PostPlatformMetaRules to enforce URL validation for Pinterest links. - Add tests for clearing Pinterest title and link, and for rejecting invalid links during scheduling. This refactor improves the handling of Pinterest metadata and enhances user experience by ensuring proper validation and error handling. * Add validation messages and attributes for Pinterest meta fields - Introduced custom validation messages and friendly attribute names for Pinterest link and title fields in PostPlatformMetaRules. - Updated StorePostRequest, UpdatePostRequest, and related tools to utilize these new messages and attributes. - Enhanced tests to assert correct error messages for invalid Pinterest links and title length constraints. This update improves user feedback during post creation and editing, ensuring clarity in validation errors. * Remove click.prevent directive from Pinterest link in PinterestPreview component. This change simplifies the link behavior, allowing default click actions to occur, which may enhance user interaction with the Pinterest link. * Update validation error messages for Pinterest meta fields in tests - Refined the assertions in PostApiPlatformMetaTest to include localized validation messages for Pinterest title and link fields. - Ensured that error messages reflect the updated validation rules, enhancing clarity for users during post creation and editing. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 21:15:50 +00:00
'title' => 'Título',
'title_placeholder' => 'Título opcional do pin',
'link' => 'Link de destino',
'link_placeholder' => 'https://exemplo.com',
'link_invalid' => 'Informe um link de destino válido (http ou https).',
'title_max' => 'O título pode ter no máximo 100 caracteres.',
'link_max' => 'O link de destino pode ter no máximo 2048 caracteres.',
],
'discord' => [
'settings' => 'Configurações do Discord',
'posting_to' => 'Publicando em',
'channel' => 'Canal',
'select_channel' => 'Selecione um canal',
'loading_channels' => 'Carregando canais…',
'search_channel' => 'Buscar canais…',
'no_channels' => 'Nenhum canal encontrado.',
'channel_required' => 'Selecione um canal do Discord para publicar este post.',
'mentions' => 'Menções',
'search_mention' => 'Mencione um cargo ou membro…',
'embeds' => 'Embeds',
'embed' => 'Embed',
'add_embed' => 'Adicionar embed',
'embed_title' => 'Título do embed',
'embed_description' => 'Descrição do embed',
'embed_url' => 'URL do embed',
'embed_image' => 'URL da imagem',
'embed_color' => 'Cor',
],
'warnings' => [
'no_variant' => 'Escolha um tipo de publicação para continuar.',
'requires_media' => 'Este tipo exige pelo menos uma imagem ou vídeo.',
'max_files_exceeded' => 'Este tipo aceita até :max arquivos (você tem :current).',
'min_files_required' => 'Este tipo exige pelo menos :min arquivos (você tem :current).',
'no_video_allowed' => 'Este tipo não aceita vídeos.',
'no_image_allowed' => 'Este tipo aceita apenas vídeos.',
'no_document_allowed' => 'Este tipo não aceita documentos PDF.',
'no_mixed_media' => 'Não dá pra combinar imagens e um vídeo no mesmo post.',
'document_not_alone' => 'Um PDF precisa ser publicado sozinho, sem outras imagens ou vídeos.',
'gif_not_allowed' => 'Esta rede não aceita GIF. Remova o GIF ou escolha outra rede.',
'image_too_large' => 'A imagem passa do limite de :max (a sua tem :current).',
'video_too_large' => 'O vídeo passa do limite de :max (o seu tem :current).',
'document_too_large' => 'O PDF passa do limite de :max (o seu tem :current).',
'video_too_long' => 'O vídeo dura :current, mas este tipo permite no máximo :max.',
'aspect_ratio_too_narrow' => 'A proporção :current está muito alta (mínimo :min).',
'aspect_ratio_too_wide' => 'A proporção :current está muito larga (máximo :max).',
],
2026-01-26 13:55:27 +00:00
],
'status' => [
'pending' => 'Pendente',
2026-01-26 13:55:27 +00:00
'draft' => 'Rascunho',
'scheduled' => 'Agendado',
'publishing' => 'Publicando',
'retrying' => 'Tentando novamente',
2026-01-26 13:55:27 +00:00
'published' => 'Publicado',
'partially_published' => 'Parcialmente Publicado',
'failed' => 'Falhou',
],
'descriptions' => [
'draft' => 'Posts aguardando agendamento',
'scheduled' => 'Posts agendados para publicação',
'published' => 'Posts já publicados',
],
'ai' => [
'generate' => [
'button_tooltip' => 'Gerar com IA',
'title' => 'Gerar post com IA',
'description' => 'Descreva sobre o que o post deve ser. A IA vai usar o contexto da sua marca pra escrever.',
'prompt_label' => 'Sobre o que é esse post?',
'prompt_placeholder' => 'ex: anunciar nossa nova feature de geração de imagens pra carrosséis',
'preview_label' => 'Prévia',
'start' => 'Gerar',
'apply' => 'Usar este conteúdo',
'retry' => 'Tentar de novo',
'cancel' => 'Cancelar',
fix: subscribe to AI generation channel before dispatching the job (#269) * fix: subscribe to AI generation channel before dispatching the job AiGenerateDialog posted the generate request and only subscribed to the broadcast channel once the response came back. Reverb's plain Channel (vendor/laravel/reverb/.../Channels/Channel.php) delivers broadcasts by iterating the in-memory connection list at send time only — no history, no replay. If the queued job started streaming text_delta/stream_end events before the private-channel subscribe handshake finished, those events were gone for good and the dialog hung on 'streaming' forever. Generate the generation_id client-side, await channel subscription confirmation (Echo's channel.subscribed(), backed by pusher:subscription_succeeded) before sending the generate request, and have the backend use the client-supplied id instead of minting its own. Related to #218. * fix: handle subscribe failure, dialog-close race, and i18n hardcoded strings Code review on the previous commit surfaced real gaps: - useAiStream only handled the subscribe-success path (.subscribed()). A definitive pusher:subscription_error (expired session, CSRF mismatch) fell through to the 5s timeout and was treated as success, reproducing the exact #218 hang via a different trigger. subscribe() now returns a boolean: true on confirm-or-ambiguous-timeout (proceed optimistically), false only on an explicit error (don't dispatch work nothing will ever hear). - AiGenerateDialog now bails without dispatching if the dialog is closed while awaiting subscription confirmation (up to 5s), and unsubscribes on a post-subscribe dispatch failure so stale listeners can't flip status to 'completed' after an error was already shown. - Extracted aiGenerationChannel() so the frontend has one place to update if the channel format changes, and cross-referenced the two remaining backend copies (PostAiGenerateController, StreamPostContent). - Replaced the two hardcoded English fallback error strings with i18n keys (posts.ai.generate.errors.*) across all 16 locales — this app only ships translated copy. - Simplified subscribe(): dropped a redundant "already settled" guard and the clearTimeout bookkeeping — Promise.resolve() already no-ops after the first call, so there was nothing to guard. * fix: subscribe before dispatch in AI post creation and image regeneration flows Extracts subscribePrivateChannel as a shared helper and applies the same subscribe-before-dispatch fix from the post edit flow (issue #218) to the other two flows that stream over private channels. Post creation wizard moves the StreamPostCreation dispatch out of AiPostWizard.vue and into Loading.vue, so the channel is subscribed before the job that broadcasts on it is dispatched — previously a full page navigation happened between dispatch and subscribe with no timeout fallback, so a lost event hung the page forever. * refactor: trim explanatory comments added in the subscribe-before-dispatch fix Rationale lives in the commit history, not scattered inline comments. * refactor: trim remaining explanatory comments from the subscribe-before-dispatch fix Same cleanup as the previous commit, applied to the files from the original post-edit-flow fix. * fix: dedup post-creation dispatch, surface field-level AI errors, harden edge cases Uses ShouldBeUnique on StreamPostCreation (scoped per-user) instead of an ad-hoc cache guard, matching the pattern already used by PublishToSocialPlatform and VerifyUpcomingPostConnections. Field-level validation errors (prompt/instruction) now render inline via InputError, matching how every other form in the app surfaces them, instead of a generic status banner. Also fixes a 422 response missing the `errors` key that Inertia's client silently swallows, adds error handling around the wizard's navigation to the loading page, and extracts the duplicated error-message parsing into a shared helper.
2026-08-10 19:06:37 +00:00
'errors' => [
'start_failed' => 'Não foi possível iniciar a geração.',
'generation_failed' => 'A geração por IA falhou.',
],
],
'review' => [
'button_tooltip' => 'Revisar com IA',
'title' => 'Revisar post com IA',
'description' => 'A IA analisa gramática, ortografia e clareza. Aplique as sugestões com as quais você concorda.',
'loading' => 'Revisando seu texto...',
'no_issues' => 'Nenhum problema encontrado. Tudo certo.',
'original' => 'Original',
'suggestion' => 'Sugestão',
'apply' => 'Aplicar',
'apply_all' => 'Aplicar todas',
'applied' => 'Aplicada',
'cancel' => 'Cancelar',
],
'image_regenerate' => [
'button' => 'Ajustar',
'title' => 'Ajustar imagem com IA',
'description' => 'Descreva a correção. A nova imagem substitui a atual e mantém a posição no carrossel.',
'instruction_label' => 'Instrução',
'instruction_placeholder' => 'ex: Corrigir o erro no título e deixar o fundo mais claro.',
'processing' => 'Regenerando imagem... isso pode levar alguns segundos.',
'submit' => 'Regenerar imagem',
'cancel' => 'Cancelar',
'success' => 'Imagem atualizada. A nova versão substituiu a anterior no post.',
'fallback_title' => 'Melhore esta copy da imagem',
'errors' => [
'required' => 'A instrução é obrigatória.',
'start_failed' => 'Não foi possível iniciar a regeneração.',
'unavailable' => 'Não foi possível regenerar esta imagem agora.',
'timeout' => 'A regeneração está demorando mais que o esperado. Tente de novo em instantes.',
'media_not_found' => 'Mídia não encontrada.',
'not_ai_media' => 'Só é possível regenerar mídia gerada por IA.',
],
],
'templates' => [
'image_card' => [
'name' => 'Post com imagem',
'description' => 'Imagem com IA, título e legenda.',
],
'carousel' => [
'name' => 'Carrossel',
'description' => 'Carrossel de vários slides com legenda.',
],
'tweet_card' => [
'name' => 'Card de tweet',
'description' => 'Seu post no estilo de um post do X/Twitter.',
],
'tweet_card_image' => [
'name' => 'Card de tweet com foto',
'description' => 'Seu post como card do X/Twitter sobre uma foto desfocada.',
],
],
],
'show' => [
'title' => 'Detalhes do Post',
'edit' => 'Editar',
'back' => 'Voltar',
'no_content' => 'Sem legenda',
'platforms' => 'Plataformas',
'no_platforms' => 'Nenhuma plataforma selecionada.',
'view_on_platform' => 'Ver na plataforma',
'published_on' => 'Publicado em :date',
'scheduled_for' => 'Agendado para :date',
'draft' => 'Rascunho',
'status_pending' => 'Pendente',
'metrics' => 'Métricas',
'metrics_loading' => 'Carregando métricas…',
'metrics_unavailable' => 'Métricas ainda não disponíveis para esta plataforma.',
'metrics_empty' => 'Nenhuma métrica retornada.',
],
2026-01-26 13:55:27 +00:00
'edit' => [
'title' => 'Editar Post',
'view_title' => 'Visualizar Post',
'labels' => 'Etiqueta',
'signatures' => 'Assinaturas',
2026-01-26 13:55:27 +00:00
'schedule' => 'Agendar',
'delete' => 'Excluir',
'schedule_for' => 'Agendar para',
'unschedule' => 'Desagendar',
2026-01-26 13:55:27 +00:00
'saving' => 'Salvando...',
'saved' => 'Salvo',
'draft' => 'Rascunho',
2026-01-26 13:55:27 +00:00
'media' => 'Mídia',
'add_media' => 'Adicionar mídia',
2026-01-26 13:55:27 +00:00
'caption' => 'Legenda',
'caption_placeholder' => 'Escreva sua legenda...',
'compose_title' => 'Crie um post',
'compose_subtitle' => 'Componha sua mensagem e adicione mídia',
'preview_empty' => [
'title' => 'Nenhuma plataforma selecionada',
'description' => 'Selecione uma plataforma para publicar e ver o preview.',
],
'drop_zone_title' => 'Adicionar mídia',
'drop_zone_subtitle' => 'Arraste arquivos ou clique para selecionar',
'add' => 'Adicionar',
'publish_to' => 'Publicar em',
'organize' => 'Organizar',
'no_labels' => 'Nenhuma etiqueta criada ainda',
'pick_time' => 'Escolher horário',
'pick_time_past' => 'Escolha uma data e hora no futuro.',
'post_now' => 'Publicar agora',
'time' => 'Horário',
'cancel' => 'Cancelar',
'schedule_date' => 'Data de agendamento',
'view_on_platform' => 'Ver na plataforma',
'platform_status' => 'Status da plataforma',
'compliance_incomplete' => 'Algumas configurações de plataforma estão incompletas ou incompatíveis com a mídia anexada.',
'compliance' => [
'requires_content_or_media' => 'Adicione texto ou mídia para publicar.',
'requires_media' => 'Adicione uma imagem ou vídeo para publicar aqui.',
'too_many_files' => 'Apenas :max arquivo(s) permitido(s) para este formato.',
'too_few_files' => 'Adicione pelo menos :min arquivos para este formato.',
'no_videos' => 'Apenas imagens são permitidas neste formato.',
'no_images' => 'Apenas vídeos são permitidos neste formato.',
'no_mixed_media' => 'Não é possível combinar imagem e vídeo na mesma publicação.',
'no_gifs' => 'GIFs não são suportados aqui.',
'no_documents' => 'Documentos PDF não são suportados por este formato.',
'document_not_alone' => 'Um PDF precisa ser publicado sozinho, sem outras imagens ou vídeos.',
'video_too_large' => 'Vídeo excede o limite de tamanho desta plataforma.',
'video_too_long' => 'Vídeo deve ter menos de :seconds segundos neste formato.',
'image_too_large' => 'Imagem excede o limite de tamanho desta plataforma.',
'document_too_large' => 'O PDF excede o limite de tamanho desta plataforma.',
'aspect_ratio_invalid' => 'A proporção da imagem não é suportada por este formato.',
'no_content_type' => 'Escolha um tipo de conteúdo para esta plataforma.',
'requires_text' => 'Adicione texto — este formato precisa de um título.',
],
'publishing' => 'Publicando...',
'publishing_overlay_title' => 'Seu post está sendo publicado',
'publishing_overlay_subtitle' => 'Isso pode levar alguns instantes. Você pode sair desta página sem problemas.',
'scheduled_overlay_title' => 'Este post está agendado',
'scheduled_overlay_subtitle' => 'Agendado para :date. Desagende para fazer alterações.',
'unschedule_cta' => 'Desagendar para editar',
'tabs' => [
'compose' => 'Escrever',
'preview' => 'Pré-visualização',
'channels' => 'Canais',
'comments' => 'Comentários',
'comments_empty' => 'Nenhum comentário ainda.',
],
'media_picker' => [
'title' => 'Escolher da galeria',
'search' => 'Buscar mídia...',
'empty' => 'Sua galeria ainda está vazia',
'cancel' => 'Cancelar',
'add' => 'Adicionar',
'add_count' => 'Adicionar :count',
],
'alt_text' => [
'label' => 'Texto alternativo',
'edit' => 'Editar texto alternativo',
'placeholder' => 'Descreva esta imagem para pessoas que usam leitores de tela',
'hint' => 'Publicado nas plataformas que oferecem suporte a descrições de imagem.',
'save' => 'Salvar',
],
'emoji_picker' => [
'search' => 'Buscar emoji',
'empty' => 'Nenhum emoji encontrado',
'recent' => 'Usados recentemente',
'smileys' => 'Sorrisos e emoções',
'people' => 'Pessoas e corpo',
'nature' => 'Animais e natureza',
'food' => 'Comidas e bebidas',
'activities' => 'Atividades',
'travel' => 'Viagens e lugares',
'objects' => 'Objetos',
'symbols' => 'Símbolos',
'flags' => 'Bandeiras',
],
'status' => [
'pending' => 'Pendente',
'scheduled' => 'Agendado',
'published' => 'Publicado',
'publishing' => 'Publicando...',
'retrying' => 'Tentando novamente...',
'failed' => 'Falhou',
],
2026-01-26 13:55:27 +00:00
'delete_modal' => [
'title' => 'Excluir Post',
'description' => 'Tem certeza que deseja excluir este post? Esta ação não pode ser desfeita.',
'action' => 'Excluir',
'cancel' => 'Cancelar',
],
'sync_enable' => [
'title' => 'Ativar sincronização?',
'description' => 'Todas as plataformas compartilharão o mesmo conteúdo. Qualquer edição personalizada feita em plataformas individuais será substituída pelo conteúdo atual.',
'cancel' => 'Cancelar',
'action' => 'Ativar sincronização',
],
'sync_disable' => [
'title' => 'Desativar sincronização?',
'description' => 'Cada plataforma manterá seu conteúdo atual, mas edições futuras serão aplicadas apenas à plataforma que você estiver editando.',
'customize_note' => 'Você poderá personalizar o conteúdo para cada plataforma individualmente.',
'cancel' => 'Cancelar',
'action' => 'Desativar sincronização',
],
'platforms_dialog' => [
'title' => 'Selecionar Plataformas',
'description' => 'Escolha em quais plataformas publicar este post.',
],
'signatures_modal' => [
'search' => 'Buscar assinaturas...',
'no_results' => 'Nenhuma assinatura encontrada.',
],
2026-01-26 13:55:27 +00:00
'validation' => [
'select_board' => 'Selecione uma pasta',
'images_not_supported' => 'Imagens não suportadas',
'videos_not_supported' => 'Vídeos não suportados',
'max_images' => 'Máx :count imagens',
'requires_media' => 'Requer mídia',
'requires_content' => 'Texto é obrigatório',
2026-01-26 13:55:27 +00:00
'exceeded' => ':count excedido',
'does_not_support_images' => ':platform não suporta imagens',
'supports_up_to_images' => ':platform suporta até :count imagens',
'does_not_support_videos' => ':platform não suporta vídeos',
],
],
'content_types' => [
'instagram_feed' => [
'label' => 'Post do Feed',
'description' => 'Aparece no seu feed e perfil',
],
'instagram_reel' => [
'label' => 'Reels',
'description' => 'Vídeo curto de até 15 minutos',
2026-01-26 13:55:27 +00:00
],
'instagram_story' => [
'label' => 'Story',
'description' => 'Desaparece após 24 horas',
],
'linkedin_post' => [
'label' => 'Post',
'description' => 'Post padrão — imagem, várias imagens, vídeo ou PDF',
2026-01-26 13:55:27 +00:00
],
'linkedin_page_post' => [
'label' => 'Post',
'description' => 'Post padrão — imagem, várias imagens, vídeo ou PDF',
],
2026-01-26 13:55:27 +00:00
'facebook_post' => [
'label' => 'Post',
'description' => 'Post padrão na sua página',
],
'facebook_reel' => [
'label' => 'Reels',
'description' => 'Vídeo curto de até 90 segundos',
],
'facebook_story' => [
'label' => 'Story',
'description' => 'Story em vídeo vertical, até 60 segundos',
2026-01-26 13:55:27 +00:00
],
'tiktok_video' => [
'label' => 'Vídeo',
'description' => 'Conteúdo de vídeo curto',
],
'tiktok_photo' => [
'label' => 'Carrossel de fotos',
'description' => 'Até 35 fotos em um carrossel deslizável',
],
2026-01-26 13:55:27 +00:00
'youtube_short' => [
'label' => 'Short',
'description' => 'Vídeo vertical de até 3 minutos',
2026-01-26 13:55:27 +00:00
],
'x_post' => [
'label' => 'Post',
'description' => 'Tweet com texto e mídia',
],
'threads_post' => [
'label' => 'Post',
'description' => 'Post de texto com mídia opcional',
],
'pinterest_pin' => [
'label' => 'Pin',
'description' => 'Pin de imagem padrão',
2026-01-26 13:55:27 +00:00
],
'pinterest_video_pin' => [
'label' => 'Pin de Vídeo',
'description' => 'Pin de vídeo (4s - 15min)',
2026-01-26 13:55:27 +00:00
],
'pinterest_carousel' => [
'label' => 'Carrossel',
'description' => 'Carrossel multi-imagem (2-5 imagens)',
2026-01-26 13:55:27 +00:00
],
'bluesky_post' => [
'label' => 'Post',
'description' => 'Post de texto com imagens opcionais',
],
'mastodon_post' => [
'label' => 'Post',
'description' => 'Post de texto com mídia opcional',
],
'telegram_post' => [
'label' => 'Post',
'description' => 'Post de texto com mídia opcional',
],
'discord_message' => [
'label' => 'Mensagem',
'description' => 'Mensagem para um canal do Discord com mídia e embeds opcionais',
],
2026-01-26 13:55:27 +00:00
],
'platforms' => [
'linkedin' => 'LinkedIn',
'linkedin-page' => 'Página do LinkedIn',
'x' => 'X',
'tiktok' => 'TikTok',
'youtube' => 'YouTube Shorts',
'facebook' => 'Página do Facebook',
'instagram' => 'Instagram',
'threads' => 'Threads',
'pinterest' => 'Pinterest',
'bluesky' => 'Bluesky',
'mastodon' => 'Mastodon',
],
'flash' => [
'scheduled' => 'Post agendado com sucesso!',
'deleted' => 'Post excluído com sucesso!',
'duplicated' => 'Post duplicado como rascunho.',
'cannot_edit_finalized' => 'Este post já foi processado e não pode ser republicado. Duplique-o para tentar de novo.',
'cannot_delete_published' => 'Posts publicados não podem ser excluídos.',
2026-01-26 13:55:27 +00:00
'connect_first' => 'Conecte pelo menos uma rede social antes de criar um post.',
],
'errors' => [
'account_disconnected' => 'Conta social está desconectada',
2026-03-31 00:18:07 +00:00
'account_inactive' => 'Conta social está desativada',
'account_token_expired' => 'Sessão da conta social expirou — reconecte a conta',
fix: Pinterest video processing timeout — longer poll + retry (#246) * fix: give Pinterest video processing more time and retry on timeout A valid ~54s video pin failed after ~90s of polling while Pinterest was still processing. Extend the poll window to ~5 minutes and treat timeout as platform unavailable so PublishToSocialPlatform reschedules instead of failing the post on the first attempt. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: use Laravel Sleep for Pinterest media processing polls Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: inline Pinterest video processing poll constants Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: map Pinterest media upload statuses to an enum Use the official MediaUploadStatus values (registered, processing, succeeded, failed) instead of comparing raw strings in the publisher. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: trim Pinterest media processing docblock * fix: cap platform-unavailable retries and recover stuck retrying posts Stop infinite reschedules after 6 attempts with a user-safe failure message, keep technical detail in error_context, recover Retrying platforms in social:recover-stuck-posts, and drop unused isTerminal(). Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: remove unused failedCount in RecoverStuckPosts Co-authored-by: Cursor <cursoragent@cursor.com> * fix: skip final Pinterest poll sleep and localize recover timeout Co-authored-by: Cursor <cursoragent@cursor.com> * fix: raise publish job timeout headroom and ignore already-failed platforms Give social publish jobs 15 minutes so Pinterest media polling fits under the worker limit, bump Horizon/redis retry_after above that timeout, and skip handle/failed when the platform is already Failed so delayed jobs cannot revive posts recovered by social:recover-stuck-posts. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: restore social-publishing and ai-assistant horizon supervisors Co-authored-by: Cursor <cursoragent@cursor.com> * fix: harden Pinterest 401 handling, unique publish jobs, and recover JSON Treat media-status 401 as TokenExpired, make PublishToSocialPlatform unique per platform+attempt so retries still queue, and persist recover error_context via Eloquent casts instead of manual json_encode. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: mass-update stuck post platforms without per-row each Eloquent query updates already bind JSON arrays correctly here, so one UPDATE is enough — no manual json_encode and no N model writes. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: simplify Pinterest media processing poll loop Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: simplify publish job retry and terminal status checks Co-authored-by: Cursor <cursoragent@cursor.com> * fix: do not finalize posts while platforms are still retrying Co-authored-by: Cursor <cursoragent@cursor.com> * test: cover Pinterest timeout, unique jobs, and recover edge cases Co-authored-by: Cursor <cursoragent@cursor.com> * fix: retry Pinterest media poll on connection errors and tighten tests Co-authored-by: Cursor <cursoragent@cursor.com> * fix: remove ineffective TypeError import that breaks CI Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 23:49:37 +00:00
'platform_unavailable' => 'A plataforma está temporariamente indisponível. Vamos tentar de novo em breve.',
'platform_unavailable_exhausted' => 'A plataforma continuou indisponível após várias tentativas. Tente de novo mais tarde.',
'publishing_timed_out' => 'A publicação excedeu o tempo limite. Tente novamente.',
2026-01-26 13:55:27 +00:00
],
'delete' => [
'title' => 'Excluir post?',
'description' => 'Esta ação não pode ser desfeita. O post e todas as suas mídias serão removidos permanentemente.',
'confirm' => 'Sim, excluir',
'cancel' => 'Cancelar',
],
'create' => [
'title' => 'Criar novo post',
'description' => 'Escolha como quer começar.',
'scratch_title' => 'Começar do zero',
'scratch_description' => 'Abre um post em branco pra você escrever tudo.',
'ai_title' => 'Gerar com IA',
'ai_description' => 'Descreva o que quer e a IA gera o conteúdo pra você.',
'ai_configure_description' => 'Escolha o formato e descreva o post que quer criar.',
'ai_pick_template_description' => 'Escolha um estilo para o seu post gerado por IA.',
'coming_soon' => 'Em breve',
'preview' => [
'image_title' => 'Título da imagem',
'image_body' => 'Texto da imagem',
],
'steps' => [
'template_picker_title' => 'Escolha um estilo',
'format_title' => 'Escolha um formato',
'format_description' => 'Selecione o tipo de post que deseja criar.',
'account_title' => 'Escolha uma conta',
'account_description' => 'Selecione a conta social para publicar.',
'media_title' => 'Opções de mídia',
'media_carousel' => 'Quantos slides?',
'media_optional' => 'Incluir imagens?',
'media_optional_label' => 'Quantas imagens?',
'media_none' => 'Nenhuma',
'media_count_label' => 'Número de imagens',
'brand_colors_label' => 'Cores da marca',
'brand_colors_description' => 'Aplica a paleta da sua marca nas imagens geradas. Desative para deixar a IA escolher as cores que combinam com o conteúdo.',
'prompt_title' => 'Descreva seu post',
'prompt_label' => 'Sobre o que é este post?',
'prompt_placeholder' => 'Ex. Anunciar nossa nova função de carrossel para o Instagram',
'preview_error' => 'Algo deu errado. Por favor, tente novamente.',
'loading_page_title' => 'Gerando seu post',
'loading_eta' => 'Tempo estimado: cerca de :minutes.',
'loading_eta_minute_one' => '1 minuto',
'loading_eta_minute_other' => ':count minutos',
'loading_leave_title' => 'Você pode continuar trabalhando.',
'loading_leave_body' => 'A gente te avisa assim que o post ficar pronto.',
'loading_leave_cta' => 'Ir pro calendário',
'loading_create_another_cta' => 'Criar outro post',
'loading_tip_credits' => 'Cada imagem AI consome cerca de 15 créditos.',
'loading_tip_edit' => 'Você poderá editar tudo quando o post ficar pronto.',
'loading_tip_draft' => 'Posts gerados vão direto pros seus rascunhos.',
'loading_tip_brand' => 'Ajuste sua marca pra influenciar os próximos posts.',
'loading_tip_carousel' => 'Carrosséis geram um slide por imagem solicitada.',
'loading_tip_quality' => 'A qualidade equilibra velocidade e custo.',
'create' => 'Criar post',
'back' => 'Voltar',
'next' => 'Continuar',
'cancel' => 'Cancelar',
'discard' => 'Descartar',
'retry' => 'Tentar novamente',
'no_platforms' => 'Nenhuma conta conectada',
'connect_first' => 'Conecte pelo menos uma conta social para usar a geração com IA.',
'no_account_for_template' => 'Conecte uma conta compatível para usar este template.',
'format' => [
'instagram_feed' => 'Post no Feed do Instagram',
'instagram_carousel' => 'Carrossel do Instagram',
'linkedin_post' => 'Post no LinkedIn',
'linkedin_page_post' => 'Post em Página do LinkedIn',
'x_post' => 'Post no X',
'bluesky_post' => 'Post no Bluesky',
'threads_post' => 'Post no Threads',
'mastodon_post' => 'Post no Mastodon',
'telegram_post' => 'Post no Telegram',
'discord_message' => 'Mensagem do Discord',
'facebook_post' => 'Post no Facebook',
'pinterest_pin' => 'Pin no Pinterest',
'instagram_story' => 'Story do Instagram',
'facebook_story' => 'Story do Facebook',
],
],
],
2026-01-26 13:55:27 +00:00
];