Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
< ? php
return [
'title' => 'Publications' ,
'search' => 'Rechercher des publications...' ,
'all_posts' => 'Toutes les publications' ,
'new_post' => 'Nouvelle publication' ,
'no_posts' => 'Aucune publication trouvée' ,
'no_search_results' => 'Aucune publication ne correspond à votre recherche' ,
'try_different_search' => 'Essayez un autre mot-clé ou effacez la recherche.' ,
'start_creating' => 'Commencez par créer votre première publication.' ,
'filter_by_label' => 'Filtrer par étiquette' ,
'label_search_placeholder' => 'Rechercher des étiquettes...' ,
'no_labels' => 'Aucune étiquette trouvée.' ,
'clear_label_filter' => 'Effacer le filtre d\'étiquette' ,
'table' => [
'post' => 'Publication' ,
'status' => 'Statut' ,
'content' => 'Contenu' ,
'platforms' => 'Plateformes' ,
'labels' => 'Étiquettes' ,
'scheduled_at' => 'Date' ,
'actions' => '' ,
],
'manage_posts' => 'Gérez toutes vos publications' ,
'delete_confirm' => 'Voulez-vous vraiment supprimer cette publication ?' ,
'by' => 'par' ,
'actions' => [
'view' => 'Voir la publication' ,
'delete' => 'Supprimer' ,
'duplicate' => 'Dupliquer' ,
'copy_id' => 'Copier l\'ID' ,
'copied' => 'ID copié dans le presse-papiers' ,
],
'form' => [
'post_type' => 'Type de publication' ,
'board' => 'Tableau' ,
'select_board' => 'Sélectionner un tableau' ,
'search_board' => 'Rechercher un tableau...' ,
'no_board_found' => 'Aucun tableau trouvé' ,
'media' => 'Médias' ,
'min' => 'Min' ,
'uploading' => 'Import en cours...' ,
'drop_to_upload' => 'Déposez pour importer' ,
'drag_and_drop' => 'Glissez-déposez ou cliquez pour importer' ,
'photos_and_videos' => 'Photos et vidéos' ,
'photos_only' => 'Photos uniquement' ,
'videos_only' => 'Vidéos uniquement' ,
'drag_to_reorder' => 'Glissez pour réorganiser' ,
'caption' => 'Légende' ,
'write_caption' => 'Rédigez votre légende...' ,
'content_exceeds_platform' => ':platform : trop long de :over caractères (max :limit).' ,
'tiktok' => [
'settings' => 'Paramètres TikTok' ,
'variant_label' => 'Type de publication' ,
'variant' => [
'video' => 'Vidéo' ,
'photo' => 'Carrousel photo' ,
],
'posting_to' => 'Publier sur' ,
'privacy_level' => 'Qui peut voir cette vidéo ?' ,
'privacy_placeholder' => 'Sélectionnez qui peut voir cette publication' ,
'privacy' => [
'public' => 'Public pour tous' ,
'friends' => 'Amis qui s\'abonnent mutuellement' ,
'followers' => 'Abonnés' ,
'private' => 'Moi uniquement' ,
'private_disabled_branded' => 'La visibilité d\'un contenu de marque ne peut pas être définie sur privé.' ,
],
'privacy_hint' => 'Les options disponibles dépendent des paramètres de votre compte TikTok.' ,
'auto_add_music' => 'Ajouter automatiquement de la musique' ,
'auto_add_music_hint' => 'Cette fonctionnalité est disponible uniquement pour les photos. Elle ajoutera une musique par défaut que vous pourrez changer plus tard.' ,
'yes' => 'Oui' ,
'no' => 'Non' ,
'allow_users' => 'Autoriser les utilisateurs à :' ,
'comments' => 'Commenter' ,
'duet' => 'Duo' ,
'stitch' => 'Stitch' ,
'is_aigc' => 'Vidéo réalisée avec l\'IA' ,
'disclose' => 'Divulguer le contenu de la vidéo' ,
'disclose_hint' => 'Activez pour indiquer que cette vidéo fait la promotion de biens ou de services en échange d\'une contrepartie. Votre vidéo peut vous promouvoir, promouvoir un tiers, ou les deux.' ,
'promotional_organic_title' => 'Votre photo/vidéo sera étiquetée « Contenu promotionnel ».' ,
'promotional_paid_title' => 'Votre photo/vidéo sera étiquetée « Partenariat rémunéré ».' ,
'promotional_description' => 'Cela ne peut pas être modifié une fois votre vidéo publiée.' ,
'compliance_incomplete' => 'Vous devez indiquer si votre contenu fait votre propre promotion, celle d\'un tiers, ou les deux.' ,
'privacy_required' => 'Le niveau de confidentialité TikTok est requis lors de la publication.' ,
'branded_cleared_private' => 'La confidentialité a été réinitialisée car un contenu de marque ne peut pas être privé.' ,
'interaction_disabled_by_creator' => 'Désactivé dans les paramètres de votre compte TikTok' ,
'max_duration_exceeded' => 'La vidéo dure :duration s, mais ce compte ne peut publier que des vidéos jusqu\'à :max s.' ,
'processing_hint' => 'Après la publication, le traitement du contenu et son apparition sur votre profil TikTok peuvent prendre quelques minutes.' ,
'brand_organic' => 'Votre marque' ,
'brand_organic_hint' => 'Vous faites votre propre promotion ou celle de votre marque. Cette vidéo sera classée comme Contenu de marque organique.' ,
'brand_content' => 'Contenu de marque' ,
'brand_content_hint' => 'Vous faites la promotion d\'une autre marque ou d\'un tiers. Cette vidéo sera classée comme Contenu de marque.' ,
'compliance' => [
'agree' => 'En publiant, vous acceptez la' ,
'music_usage' => 'Confirmation d\'utilisation de la musique' ,
'and' => 'et la' ,
'branded_policy' => 'Politique de contenu de marque de TikTok' ,
],
],
'instagram' => [
'settings' => 'Paramètres Instagram' ,
'posting_to' => 'Publier sur' ,
'variant_label' => 'Type de publication' ,
'variant' => [
'feed' => 'Publication' ,
'reel' => 'Reel' ,
'story' => 'Story' ,
],
'aspect_label' => 'Format' ,
'aspect' => [
'square' => 'Carré (1:1)' ,
'portrait' => 'Portrait (4:5)' ,
'landscape' => 'Paysage (16:9)' ,
'original' => 'Original' ,
],
],
'facebook' => [
'settings' => 'Paramètres Facebook' ,
'posting_to' => 'Publier sur' ,
'variant_label' => 'Type de publication' ,
'variant' => [
'post' => 'Publication' ,
'reel' => 'Reel' ,
'story' => 'Story' ,
],
'aspect_label' => 'Format' ,
'aspect' => [
'square' => 'Carré (1:1)' ,
'portrait' => 'Portrait (4:5)' ,
'landscape' => 'Paysage (16:9)' ,
'original' => 'Original' ,
],
],
'linkedin' => [
'settings' => 'Paramètres LinkedIn' ,
'settings_page' => 'Paramètres de la page LinkedIn' ,
'posting_to' => 'Publier sur' ,
'document_title' => 'Titre du document' ,
'document_title_placeholder' => 'Affiché sur votre publication de document PDF' ,
],
'pinterest' => [
'settings' => 'Paramètres Pinterest' ,
'posting_to' => 'Publier sur' ,
'variant_label' => 'Type d\'épingle' ,
'variant' => [
'pin' => 'Épingle' ,
'video_pin' => 'Épingle vidéo' ,
'carousel' => 'Carrousel' ,
],
'board' => 'Tableau' ,
'select_board' => 'Sélectionner un tableau' ,
'no_boards' => 'Aucun tableau Pinterest trouvé. Créez-en un dans votre compte Pinterest d\'abord.' ,
2026-07-25 02:02:40 +00:00
'boards_truncated' => 'Certains tableaux n’ ont pas pu être chargés. S’ il manque le vôtre, ouvrez Pinterest et réessayez.' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
'search_board' => 'Rechercher des tableaux...' ,
'no_board_found' => 'Aucun tableau ne correspond à votre recherche.' ,
'board_required' => 'Sélectionnez un tableau Pinterest pour publier cette publication.' ,
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' => 'Titre' ,
'title_placeholder' => 'Titre optionnel de l’ épingle' ,
'link' => 'Lien de destination' ,
'link_placeholder' => 'https://exemple.com' ,
'link_invalid' => 'Saisissez un lien de destination valide (http ou https).' ,
'title_max' => 'Le titre ne peut pas dépasser 100 caractères.' ,
'link_max' => 'Le lien de destination ne peut pas dépasser 2048 caractères.' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
],
'discord' => [
'settings' => 'Paramètres Discord' ,
'posting_to' => 'Publier sur' ,
'channel' => 'Salon' ,
'select_channel' => 'Sélectionner un salon' ,
'loading_channels' => 'Chargement des salons…' ,
'search_channel' => 'Rechercher des salons…' ,
'no_channels' => 'Aucun salon trouvé.' ,
'channel_required' => 'Sélectionnez un salon Discord pour publier cette publication.' ,
'mentions' => 'Mentions' ,
'search_mention' => 'Mentionner un rôle ou un membre…' ,
'embeds' => 'Embeds' ,
'embed' => 'Embed' ,
'add_embed' => 'Ajouter un embed' ,
'embed_title' => 'Titre de l\'embed' ,
'embed_description' => 'Description de l\'embed' ,
'embed_url' => 'URL de l\'embed' ,
'embed_image' => 'URL de l\'image' ,
'embed_color' => 'Couleur' ,
],
'warnings' => [
'no_variant' => 'Choisissez un type de publication pour continuer.' ,
'requires_media' => 'Ce type de publication nécessite au moins une image ou une vidéo.' ,
'max_files_exceeded' => 'Ce type de publication accepte jusqu\'à :max fichiers médias (vous en avez :current).' ,
'min_files_required' => 'Ce type de publication nécessite au moins :min fichiers médias (vous en avez :current).' ,
'no_video_allowed' => 'Ce type de publication n\'accepte pas les vidéos.' ,
'no_image_allowed' => 'Ce type de publication n\'accepte que les vidéos.' ,
'no_document_allowed' => 'Ce type de publication n\'accepte pas les documents PDF.' ,
'no_mixed_media' => 'Les images et une vidéo ne peuvent pas être combinées dans la même publication.' ,
'document_not_alone' => 'Un PDF doit être publié seul, sans autres images ou vidéos.' ,
'gif_not_allowed' => 'Cette plateforme n\'accepte pas les GIF. Supprimez le GIF ou choisissez un autre réseau.' ,
'image_too_large' => 'L\'image dépasse la limite de :max pour ce type de publication (la vôtre est de :current).' ,
'video_too_large' => 'La vidéo dépasse la limite de :max pour ce type de publication (la vôtre est de :current).' ,
'document_too_large' => 'Le PDF dépasse la limite de :max pour ce type de publication (le vôtre est de :current).' ,
'video_too_long' => 'La vidéo dure :current, mais ce type de publication autorise jusqu\'à :max.' ,
'aspect_ratio_too_narrow' => 'Le format :current est trop haut pour ce type de publication (min :min).' ,
'aspect_ratio_too_wide' => 'Le format :current est trop large pour ce type de publication (max :max).' ,
],
],
'status' => [
'pending' => 'En attente' ,
'draft' => 'Brouillon' ,
'scheduled' => 'Programmée' ,
'publishing' => 'Publication en cours' ,
'retrying' => 'Nouvelle tentative' ,
'published' => 'Publiée' ,
'partially_published' => 'Partiellement publiée' ,
'failed' => 'Échec' ,
],
'descriptions' => [
'draft' => 'Publications en attente de programmation' ,
'scheduled' => 'Publications programmées pour publication' ,
'published' => 'Publications déjà publiées' ,
],
'ai' => [
'generate' => [
'button_tooltip' => 'Générer avec l\'IA' ,
'title' => 'Générer une publication avec l\'IA' ,
'description' => 'Décrivez le sujet de la publication. L\'IA utilisera le contexte de votre marque pour la rédiger.' ,
'prompt_label' => 'De quoi parle cette publication ?' ,
'prompt_placeholder' => 'par ex. Annoncer notre nouvelle fonctionnalité de génération d\'images pour les carrousels' ,
'preview_label' => 'Aperçu' ,
'start' => 'Générer' ,
'apply' => 'Utiliser ce contenu' ,
'retry' => 'Réessayer' ,
'cancel' => 'Annuler' ,
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' => 'Impossible de démarrer la génération.' ,
'generation_failed' => 'Échec de la génération par l\'IA.' ,
],
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
],
'review' => [
'button_tooltip' => 'Relire avec l\'IA' ,
'title' => 'Relire la publication avec l\'IA' ,
'description' => 'L\'IA analyse la grammaire, l\'orthographe et la clarté. Appliquez les suggestions qui vous conviennent.' ,
'loading' => 'Relecture de votre texte...' ,
'no_issues' => 'Aucun problème détecté. C\'est parfait.' ,
'original' => 'Original' ,
'suggestion' => 'Suggestion' ,
'apply' => 'Appliquer' ,
'apply_all' => 'Tout appliquer' ,
'applied' => 'Appliqué' ,
'cancel' => 'Annuler' ,
],
'image_regenerate' => [
'button' => 'Ajuster' ,
'title' => 'Ajuster l\'image IA' ,
'description' => 'Décrivez la correction. La nouvelle image remplace l\'actuelle et conserve sa position dans le carrousel.' ,
'instruction_label' => 'Instruction' ,
'instruction_placeholder' => 'par ex. Corrigez la faute dans le titre et éclaircissez l\'arrière-plan.' ,
'processing' => 'Régénération de l\'image... cela peut prendre quelques secondes.' ,
'submit' => 'Régénérer l\'image' ,
'cancel' => 'Annuler' ,
'success' => 'Image mise à jour. La nouvelle version a remplacé la précédente dans votre publication.' ,
'fallback_title' => 'Améliorer le texte de cette image' ,
'errors' => [
'required' => 'L\'instruction est obligatoire.' ,
'start_failed' => 'Impossible de démarrer la régénération.' ,
'unavailable' => 'Impossible de régénérer cette image pour le moment.' ,
'timeout' => 'La régénération prend plus de temps que prévu. Réessayez dans un instant.' ,
'media_not_found' => 'Élément média introuvable.' ,
'not_ai_media' => 'Seuls les médias générés par l\'IA peuvent être régénérés.' ,
],
],
'templates' => [
'image_card' => [
'name' => 'Publication image' ,
'description' => 'Image IA avec un titre et une légende.' ,
],
'carousel' => [
'name' => 'Carrousel' ,
'description' => 'Carrousel à plusieurs diapositives avec une légende.' ,
],
'tweet_card' => [
'name' => 'Carte tweet' ,
'description' => 'Votre publication mise en forme comme une publication X/Twitter.' ,
],
'tweet_card_image' => [
'name' => 'Carte tweet avec photo' ,
'description' => 'Votre publication en carte X/Twitter sur une photo floutée.' ,
],
],
],
'show' => [
'title' => 'Détails de la publication' ,
'edit' => 'Modifier' ,
'back' => 'Retour' ,
'no_content' => 'Aucune légende' ,
'platforms' => 'Plateformes' ,
'no_platforms' => 'Aucune plateforme sélectionnée.' ,
'view_on_platform' => 'Voir sur la plateforme' ,
'published_on' => 'Publiée le :date' ,
'scheduled_for' => 'Programmée pour le :date' ,
'draft' => 'Brouillon' ,
'status_pending' => 'En attente' ,
'metrics' => 'Métriques' ,
'metrics_loading' => 'Chargement des métriques…' ,
'metrics_unavailable' => 'Métriques pas encore disponibles pour cette plateforme.' ,
'metrics_empty' => 'Aucune métrique renvoyée.' ,
],
'edit' => [
'title' => 'Modifier la publication' ,
'view_title' => 'Voir la publication' ,
'labels' => 'Étiquettes' ,
'no_labels' => 'Aucune étiquette créée pour le moment' ,
'schedule' => 'Programmer' ,
'pick_time' => 'Choisir l\'heure' ,
'pick_time_past' => 'Choisissez une date et une heure futures.' ,
'post_now' => 'Publier maintenant' ,
'time' => 'Heure' ,
'cancel' => 'Annuler' ,
'delete' => 'Supprimer' ,
'schedule_for' => 'Programmer pour le' ,
'schedule_date' => 'Date de programmation' ,
'unschedule' => 'Déprogrammer' ,
'saving' => 'Enregistrement...' ,
'saved' => 'Enregistré' ,
'draft' => 'Brouillon' ,
'media' => 'Médias' ,
'add_media' => 'Ajouter un média' ,
'caption' => 'Légende' ,
'caption_placeholder' => 'Rédigez votre légende...' ,
'compose_title' => 'Créer une publication' ,
'compose_subtitle' => 'Composez votre message et ajoutez des médias' ,
'preview_empty' => [
'title' => 'Aucune plateforme sélectionnée' ,
'description' => 'Sélectionnez une plateforme de publication pour voir l\'aperçu.' ,
],
'drop_zone_title' => 'Ajouter un média' ,
'drop_zone_subtitle' => 'Glissez-déposez des fichiers ou cliquez pour parcourir' ,
'add' => 'Ajouter' ,
'publish_to' => 'Publier sur' ,
'organize' => 'Organiser' ,
'signatures' => 'Signatures' ,
'view_on_platform' => 'Voir sur la plateforme' ,
'platform_status' => 'Statut de la plateforme' ,
'compliance_incomplete' => 'Certains paramètres de plateforme sont incomplets ou incompatibles avec les médias joints.' ,
'compliance' => [
'requires_content_or_media' => 'Ajoutez du texte ou un média pour publier.' ,
'requires_media' => 'Ajoutez une image ou une vidéo pour publier ici.' ,
'too_many_files' => 'Seulement :max fichier(s) autorisé(s) pour ce format.' ,
'too_few_files' => 'Ajoutez au moins :min fichiers pour ce format.' ,
'no_videos' => 'Seules les images sont autorisées pour ce format.' ,
'no_images' => 'Seules les vidéos sont autorisées pour ce format.' ,
'no_mixed_media' => 'Les images et les vidéos ne peuvent pas être combinées dans la même publication.' ,
'no_gifs' => 'Les GIF ne sont pas pris en charge ici.' ,
'no_documents' => 'Les documents PDF ne sont pas pris en charge par ce format.' ,
'document_not_alone' => 'Un PDF doit être publié seul, sans autres images ou vidéos.' ,
'video_too_large' => 'La vidéo dépasse la limite de taille pour cette plateforme.' ,
'video_too_long' => 'La vidéo doit durer moins de :seconds secondes pour ce format.' ,
'image_too_large' => 'L\'image dépasse la limite de taille pour cette plateforme.' ,
'document_too_large' => 'Le PDF dépasse la limite de taille pour cette plateforme.' ,
'aspect_ratio_invalid' => 'Le format d\'image n\'est pas pris en charge par ce format.' ,
'no_content_type' => 'Choisissez un type de contenu pour cette plateforme.' ,
'requires_text' => 'Ajoutez du texte — ce format nécessite un titre.' ,
],
'publishing' => 'Publication en cours...' ,
'publishing_overlay_title' => 'Votre publication est en cours de publication' ,
'publishing_overlay_subtitle' => 'Cela peut prendre quelques instants. Vous pouvez quitter cette page en toute sécurité.' ,
'scheduled_overlay_title' => 'Cette publication est programmée' ,
'scheduled_overlay_subtitle' => 'Programmée pour le :date. Déprogrammez-la d\'abord pour la modifier.' ,
'unschedule_cta' => 'Déprogrammer pour modifier' ,
'tabs' => [
2026-07-17 21:38:20 +00:00
'compose' => 'Rédiger' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
'preview' => 'Aperçu' ,
'channels' => 'Canaux' ,
'comments' => 'Commentaires' ,
'comments_empty' => 'Aucun commentaire pour le moment.' ,
],
'media_picker' => [
'title' => 'Choisir dans la galerie' ,
'search' => 'Rechercher un média...' ,
'empty' => 'Aucun média dans votre galerie pour le moment' ,
'cancel' => 'Annuler' ,
'add' => 'Ajouter' ,
'add_count' => 'Ajouter :count' ,
],
2026-07-11 12:45:52 +00:00
'alt_text' => [
'label' => 'Texte alternatif' ,
'edit' => 'Modifier le texte alternatif' ,
'placeholder' => 'Décrivez cette image pour les personnes utilisant un lecteur d\'écran' ,
'hint' => 'Publié sur les plateformes qui prennent en charge les descriptions d\'images.' ,
'save' => 'Enregistrer' ,
],
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
'emoji_picker' => [
'search' => 'Rechercher un émoji' ,
'empty' => 'Aucun émoji trouvé' ,
'recent' => 'Fréquemment utilisés' ,
'smileys' => 'Smileys et émotion' ,
'people' => 'Personnes et corps' ,
'nature' => 'Animaux et nature' ,
'food' => 'Nourriture et boissons' ,
'activities' => 'Activités' ,
'travel' => 'Voyage et lieux' ,
'objects' => 'Objets' ,
'symbols' => 'Symboles' ,
'flags' => 'Drapeaux' ,
],
'status' => [
'pending' => 'En attente' ,
'scheduled' => 'Programmée' ,
'published' => 'Publiée' ,
'publishing' => 'Publication en cours...' ,
'retrying' => 'Nouvelle tentative...' ,
'failed' => 'Échec' ,
],
'delete_modal' => [
'title' => 'Supprimer la publication' ,
'description' => 'Voulez-vous vraiment supprimer cette publication ? Cette action est irréversible.' ,
'action' => 'Supprimer' ,
'cancel' => 'Annuler' ,
],
'sync_enable' => [
'title' => 'Activer la synchronisation ?' ,
'description' => 'Toutes les plateformes partageront le même contenu. Toutes les modifications personnalisées apportées à des plateformes individuelles seront remplacées par le contenu actuel.' ,
'cancel' => 'Annuler' ,
'action' => 'Activer la synchronisation' ,
],
'sync_disable' => [
'title' => 'Désactiver la synchronisation ?' ,
'description' => 'Chaque plateforme conservera son contenu actuel, mais les modifications futures ne s\'appliqueront qu\'à la plateforme que vous modifiez.' ,
'customize_note' => 'Vous pourrez personnaliser le contenu de chaque plateforme individuellement.' ,
'cancel' => 'Annuler' ,
'action' => 'Désactiver la synchronisation' ,
],
'platforms_dialog' => [
'title' => 'Sélectionner les plateformes' ,
'description' => 'Choisissez les plateformes sur lesquelles publier cette publication.' ,
],
'signatures_modal' => [
'search' => 'Rechercher des signatures...' ,
'no_results' => 'Aucune signature trouvée.' ,
],
'validation' => [
'select_board' => 'Sélectionner un tableau' ,
'images_not_supported' => 'Images non prises en charge' ,
'videos_not_supported' => 'Vidéos non prises en charge' ,
'max_images' => ':count images max' ,
'requires_media' => 'Média requis' ,
'requires_content' => 'Un contenu texte est requis' ,
'exceeded' => ':count dépassé' ,
'does_not_support_images' => ':platform ne prend pas en charge les images' ,
'supports_up_to_images' => ':platform prend en charge jusqu\'à :count images' ,
'does_not_support_videos' => ':platform ne prend pas en charge les vidéos' ,
],
],
'content_types' => [
'instagram_feed' => [
'label' => 'Publication' ,
'description' => 'Apparaît dans votre fil et votre profil' ,
],
'instagram_reel' => [
'label' => 'Reel' ,
2026-07-25 00:21:39 +00:00
'description' => 'Courte vidéo jusqu\'à 15 minutes' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
],
'instagram_story' => [
'label' => 'Story' ,
'description' => 'Disparaît après 24 heures' ,
],
'linkedin_post' => [
'label' => 'Publication' ,
'description' => 'Publication standard — image unique, multi-images, vidéo ou PDF' ,
],
'linkedin_page_post' => [
'label' => 'Publication' ,
'description' => 'Publication standard — image unique, multi-images, vidéo ou PDF' ,
],
'facebook_post' => [
'label' => 'Publication' ,
'description' => 'Publication standard sur votre page' ,
],
'facebook_reel' => [
'label' => 'Reel' ,
'description' => 'Courte vidéo jusqu\'à 90 secondes' ,
],
'facebook_story' => [
'label' => 'Story' ,
'description' => 'Story vidéo verticale, jusqu\'à 60 secondes' ,
],
'tiktok_video' => [
'label' => 'Vidéo' ,
'description' => 'Contenu vidéo au format court' ,
],
'tiktok_photo' => [
'label' => 'Carrousel photo' ,
'description' => 'Jusqu\'à 35 photos en carrousel à faire défiler' ,
],
'youtube_short' => [
'label' => 'Short' ,
2026-07-23 15:06:15 +00:00
'description' => 'Vidéo verticale jusqu\'à 3 minutes' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
],
'x_post' => [
'label' => 'Publication' ,
'description' => 'Tweet avec texte et médias' ,
],
'threads_post' => [
'label' => 'Publication' ,
'description' => 'Publication texte avec média facultatif' ,
],
'pinterest_pin' => [
'label' => 'Épingle' ,
'description' => 'Épingle image standard' ,
],
'pinterest_video_pin' => [
'label' => 'Épingle vidéo' ,
'description' => 'Épingle vidéo (4 s à 15 min)' ,
],
'pinterest_carousel' => [
'label' => 'Carrousel' ,
'description' => 'Carrousel multi-images (2 à 5 images)' ,
],
'bluesky_post' => [
'label' => 'Publication' ,
'description' => 'Publication texte avec images facultatives' ,
],
'mastodon_post' => [
'label' => 'Publication' ,
'description' => 'Publication texte avec média facultatif' ,
],
'telegram_post' => [
'label' => 'Publication' ,
'description' => 'Publication texte avec média facultatif' ,
],
'discord_message' => [
'label' => 'Message' ,
'description' => 'Message vers un salon Discord avec médias et embeds facultatifs' ,
],
],
'platforms' => [
'linkedin' => 'LinkedIn' ,
'linkedin-page' => 'Page LinkedIn' ,
'x' => 'X' ,
'tiktok' => 'TikTok' ,
'youtube' => 'YouTube Shorts' ,
'facebook' => 'Page Facebook' ,
'instagram' => 'Instagram' ,
'threads' => 'Threads' ,
'pinterest' => 'Pinterest' ,
'bluesky' => 'Bluesky' ,
'mastodon' => 'Mastodon' ,
],
'flash' => [
'scheduled' => 'Publication programmée avec succès !' ,
'deleted' => 'Publication supprimée avec succès !' ,
'duplicated' => 'Publication dupliquée en brouillon.' ,
'cannot_edit_finalized' => 'Cette publication a déjà été traitée et ne peut pas être republiée. Dupliquez-la pour réessayer.' ,
'cannot_delete_published' => 'Les publications publiées ne peuvent pas être supprimées.' ,
'connect_first' => 'Connectez au moins un réseau social avant de créer une publication.' ,
],
'errors' => [
'account_disconnected' => 'Le compte social est déconnecté' ,
'account_inactive' => 'Le compte social est désactivé' ,
'account_token_expired' => 'La session du compte social a expiré — veuillez reconnecter' ,
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' => 'La plateforme est temporairement indisponible. Nouvelle tentative sous peu.' ,
'platform_unavailable_exhausted' => 'La plateforme est restée indisponible après plusieurs tentatives. Réessayez plus tard.' ,
'publishing_timed_out' => 'La publication a expiré. Veuillez réessayer.' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
],
'delete' => [
'title' => 'Supprimer la publication ?' ,
'description' => 'Cette action est irréversible. La publication et tous ses médias seront définitivement supprimés.' ,
'confirm' => 'Oui, supprimer' ,
'cancel' => 'Annuler' ,
],
'create' => [
'title' => 'Créer une nouvelle publication' ,
'description' => 'Choisissez comment vous souhaitez commencer.' ,
'scratch_title' => 'Partir de zéro' ,
'scratch_description' => 'Ouvrez une publication vierge et rédigez tout vous-même.' ,
'ai_title' => 'Générer avec l\'IA' ,
'ai_description' => 'Décrivez ce que vous voulez et l\'IA génère le contenu pour vous.' ,
'ai_configure_description' => 'Choisissez un format et décrivez la publication que vous souhaitez créer.' ,
'ai_pick_template_description' => 'Choisissez un style pour votre publication générée par l\'IA.' ,
'coming_soon' => 'Bientôt disponible' ,
'preview' => [
'image_title' => 'Titre de l\'image' ,
'image_body' => 'Corps de l\'image' ,
],
'steps' => [
'template_picker_title' => 'Choisissez un style' ,
'format_title' => 'Choisissez un format' ,
'format_description' => 'Sélectionnez le type de publication que vous souhaitez créer.' ,
'account_title' => 'Choisissez un compte' ,
'account_description' => 'Sélectionnez le compte social sur lequel publier.' ,
'media_title' => 'Options de médias' ,
'media_carousel' => 'Combien de diapositives ?' ,
'media_optional' => 'Inclure des images ?' ,
'media_optional_label' => 'Combien d\'images ?' ,
'media_none' => 'Aucune' ,
'media_count_label' => 'Nombre d\'images' ,
2026-07-18 18:12:26 +00:00
'brand_colors_label' => 'Couleurs de la marque' ,
'brand_colors_description' => 'Appliquez la palette de votre marque aux images générées. Désactivez pour laisser l’ IA choisir des couleurs adaptées au contenu.' ,
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
'prompt_title' => 'Décrivez votre publication' ,
'prompt_label' => 'De quoi parle cette publication ?' ,
'prompt_placeholder' => 'par ex. Annoncer notre nouvelle fonctionnalité de carrousel pour Instagram' ,
'preview_error' => 'Une erreur s\'est produite. Veuillez réessayer.' ,
'loading_page_title' => 'Génération de votre publication' ,
'loading_eta' => 'Temps estimé : environ :minutes.' ,
'loading_eta_minute_one' => '1 minute' ,
'loading_eta_minute_other' => ':count minutes' ,
'loading_leave_title' => 'Vous pouvez continuer à travailler.' ,
'loading_leave_body' => 'Nous vous préviendrons lorsque la publication sera prête.' ,
'loading_leave_cta' => 'Aller au calendrier' ,
'loading_create_another_cta' => 'Créer une autre publication' ,
'loading_tip_credits' => 'Chaque image IA utilise environ 15 crédits.' ,
'loading_tip_edit' => 'Vous pourrez tout modifier une fois la publication prête.' ,
'loading_tip_draft' => 'Les publications générées arrivent dans vos brouillons.' ,
'loading_tip_brand' => 'Ajustez les paramètres de votre marque pour influencer les futures publications.' ,
'loading_tip_carousel' => 'Les carrousels affichent une diapositive par image importée.' ,
'loading_tip_quality' => 'La qualité des images est réglée pour équilibrer vitesse et coût.' ,
'create' => 'Créer la publication' ,
'back' => 'Retour' ,
'next' => 'Continuer' ,
'cancel' => 'Annuler' ,
'discard' => 'Abandonner' ,
'retry' => 'Réessayer' ,
'no_platforms' => 'Aucun compte connecté' ,
'connect_first' => 'Connectez au moins un compte social pour utiliser la génération par IA.' ,
'no_account_for_template' => 'Connectez un compte compatible pour utiliser ce modèle.' ,
'format' => [
'instagram_feed' => 'Publication Instagram (fil)' ,
'instagram_carousel' => 'Carrousel Instagram' ,
'linkedin_post' => 'Publication LinkedIn' ,
'linkedin_page_post' => 'Publication de page LinkedIn' ,
'x_post' => 'Publication X' ,
'bluesky_post' => 'Publication Bluesky' ,
'threads_post' => 'Publication Threads' ,
'mastodon_post' => 'Publication Mastodon' ,
'telegram_post' => 'Publication Telegram' ,
'discord_message' => 'Message Discord' ,
'facebook_post' => 'Publication Facebook' ,
'pinterest_pin' => 'Épingle Pinterest' ,
'instagram_story' => 'Story Instagram' ,
'facebook_story' => 'Story Facebook' ,
],
],
],
];