@nextblock-cms/db 0.19.5 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,220 +1,220 @@
1
- -- Public UI copy: typography, French accents, and the keys the accessibility pass needs.
2
- --
3
- -- Data only, and safe to re-run (verified: a second run changes nothing).
4
- -- 1. French corrections match the EXACT seeded text, so edited copy is left alone.
5
- -- 2. The ellipsis pass changes a trailing three dots into one ellipsis character and
6
- -- nothing else, in edited copy too.
7
- -- 3. New keys use ON CONFLICT DO NOTHING.
8
-
9
- -- 1. French strings seeded without their accents ("Selectionnez", "Parametres", "a l'accueil").
10
- -- 43 rows. Runs first, against the original text, so receipt_finalizing gets its accent
11
- -- and its ellipsis in one step.
12
- UPDATE public.translations AS t
13
- SET translations = jsonb_set(t.translations, '{fr}', to_jsonb(fix.new_fr)),
14
- updated_at = now()
15
- FROM (
16
- VALUES
17
- ('ecommerce.variant_availability_help', 'Selectionnez une combinaison pour afficher le prix exact et la disponibilite de la variante.', 'Sélectionnez une combinaison pour afficher le prix exact et la disponibilité de la variante.'),
18
- ('ecommerce.variant_selection_required', 'Selectionnez une valeur dans chaque liste pour afficher la variante correspondante.', 'Sélectionnez une valeur dans chaque liste pour afficher la variante correspondante.'),
19
- ('bill_to', 'Facturer a', 'Facturer à'),
20
- ('ship_to', 'Livrer a', 'Livrer à'),
21
- ('invoice_settings', 'Parametres de facture', 'Paramètres de facture'),
22
- ('print_invoice_help', 'Utilisez la boite de dialogue d''impression de votre navigateur pour enregistrer cette facture en PDF.', 'Utilisez la boîte de dialogue d''impression de votre navigateur pour enregistrer cette facture en PDF.'),
23
- ('return_home', 'Retour a l''accueil', 'Retour à l''accueil'),
24
- ('receipt_finalizing', 'Finalisation de votre facture et des details du paiement...', 'Finalisation de votre facture et des détails du paiement…'),
25
- ('receipt_not_ready', 'Votre facture apparaitra ici une fois la synchronisation du paiement terminee.', 'Votre facture apparaîtra ici une fois la synchronisation du paiement terminée.'),
26
- ('tax_breakdown', 'Detail des taxes', 'Détail des taxes'),
27
- ('paid_on', 'Paye le', 'Payé le'),
28
- ('ecommerce.checkout_trial_started', 'Essai demarre', 'Essai démarré'),
29
- ('select_an_option', 'Selectionnez une option', 'Sélectionnez une option'),
30
- ('ecommerce.shipping_method_required', 'Veuillez selectionner un mode de livraison avant de continuer.', 'Veuillez sélectionner un mode de livraison avant de continuer.'),
31
- ('ecommerce.waiting_on_address_info', 'Completez votre adresse de livraison pour voir les options de livraison disponibles.', 'Complétez votre adresse de livraison pour voir les options de livraison disponibles.'),
32
- ('ecommerce.sandbox_checkout_stripe_description', 'Cette etape simulee represente le paiement Stripe pour les produits physiques.', 'Cette étape simulée représente le paiement Stripe pour les produits physiques.'),
33
- ('ecommerce.sandbox_checkout_freemius_description', 'Cette etape simulee represente le paiement Freemius pour les produits numeriques.', 'Cette étape simulée représente le paiement Freemius pour les produits numériques.'),
34
- ('ecommerce.digital_label', 'Numerique', 'Numérique'),
35
- ('ecommerce.digital_products', 'Produits numeriques', 'Produits numériques'),
36
- ('ecommerce.estimated_total', 'Total estime', 'Total estimé'),
37
- ('ecommerce.shipping_taxes_collected_on_stripe', 'La livraison et les taxes sont percues uniquement a l''etape Stripe pour les produits physiques.', 'La livraison et les taxes sont perçues uniquement à l''étape Stripe pour les produits physiques.'),
38
- ('ecommerce.freemius_checkout_description', 'Les produits numeriques utilisent le flux de paiement Freemius.', 'Les produits numériques utilisent le flux de paiement Freemius.'),
39
- ('ecommerce.checkout_billing_cycle_lifetime', 'Abonnement a vie', 'Abonnement à vie'),
40
- ('ecommerce.checkout_digital_product', 'Payer le produit numerique', 'Payer le produit numérique'),
41
- ('ecommerce.digital_subtotal', 'Sous-total numerique', 'Sous-total numérique'),
42
- ('ecommerce.freemius_multi_checkout_notice', 'Les licences Freemius se finalisent une a la fois, donc chaque produit numerique a sa propre action de paiement.', 'Les licences Freemius se finalisent une à la fois, donc chaque produit numérique a sa propre action de paiement.'),
43
- ('ecommerce.freemius_tax_notice', 'Les taxes et la conformite pour les produits numeriques sont gerees dans le paiement Freemius.', 'Les taxes et la conformité pour les produits numériques sont gérées dans le paiement Freemius.'),
44
- ('checkout_success_sync_failed', 'Nous n''avons pas encore pu finaliser votre facture. Veuillez rafraichir la page sous peu.', 'Nous n''avons pas encore pu finaliser votre facture. Veuillez rafraîchir la page sous peu.'),
45
- ('ecommerce.shipping_calculation_failed', 'Nous n''avons pas pu calculer la livraison pour le moment. Veuillez reessayer.', 'Nous n''avons pas pu calculer la livraison pour le moment. Veuillez réessayer.'),
46
- ('ecommerce.checkout_invalid_items', 'Les articles de votre commande n''ont pas pu etre traites.', 'Les articles de votre commande n''ont pas pu être traités.'),
47
- ('ecommerce.checkout_provider_items_required', 'Chaque etape de paiement doit inclure des articles associes a un fournisseur de paiement.', 'Chaque étape de paiement doit inclure des articles associés à un fournisseur de paiement.'),
48
- ('ecommerce.checkout_mixed_provider_steps', 'Les produits utilisant differents fournisseurs de paiement doivent etre achetes en etapes separees.', 'Les produits utilisant différents fournisseurs de paiement doivent être achetés en étapes séparées.'),
49
- ('ecommerce.checkout_freemius_single_item', 'Les produits Freemius doivent etre achetes un a la fois.', 'Les produits Freemius doivent être achetés un à la fois.'),
50
- ('ecommerce.checkout_internal_server_error', 'Une erreur s''est produite lors de la preparation de votre paiement. Veuillez reessayer.', 'Une erreur s''est produite lors de la préparation de votre paiement. Veuillez réessayer.'),
51
- ('ecommerce.checkout_missing_session_id', 'Nous n''avons pas trouve de session de paiement a finaliser.', 'Nous n''avons pas trouvé de session de paiement à finaliser.'),
52
- ('ecommerce.checkout_success_order_not_found', 'Nous n''avons pas trouve la commande liee a ce paiement.', 'Nous n''avons pas trouvé la commande liée à ce paiement.'),
53
- ('ecommerce.checkout_success_invalid_reference', 'Cette reference de paiement ne peut pas etre finalisee depuis cette page.', 'Cette référence de paiement ne peut pas être finalisée depuis cette page.'),
54
- ('ecommerce.checkout_success_inventory_update_failed', 'Nous n''avons pas pu mettre a jour l''inventaire pour cette commande.', 'Nous n''avons pas pu mettre à jour l''inventaire pour cette commande.'),
55
- ('ecommerce.checkout_success_status_update_failed', 'Nous n''avons pas pu mettre a jour le statut de la commande.', 'Nous n''avons pas pu mettre à jour le statut de la commande.'),
56
- ('global_search.description', 'Rechercher dans les pages, articles et produits publies.', 'Rechercher dans les pages, articles et produits publiés.'),
57
- ('global_search.recent', 'Recents', 'Récents'),
58
- ('global_search.error_description', 'Veuillez reessayer dans un instant.', 'Veuillez réessayer dans un instant.'),
59
- ('global_search.empty_title', 'Aucun resultat trouve.', 'Aucun résultat trouvé.')
60
- ) AS fix(key, old_fr, new_fr)
61
- WHERE t.key = fix.key
62
- AND t.translations->>'fr' = fix.old_fr;
63
-
64
- -- 2. "Loading..." -> "Loading…" in every language of the 12 rows seeded with three dots
65
- -- (Web Interface Guidelines: a real ellipsis character in loading and placeholder copy).
66
- -- The pattern is a character class on purpose: no backslash to lose along the way.
67
- UPDATE public.translations AS t
68
- SET translations = (
69
- SELECT jsonb_object_agg(entry.key, to_jsonb(regexp_replace(entry.value, '[.]{3}$', '…')))
70
- FROM jsonb_each_text(t.translations) AS entry(key, value)
71
- ),
72
- updated_at = now()
73
- WHERE t.key IN (
74
- 'saving',
75
- 'ecommerce.search_products',
76
- 'ecommerce.processing',
77
- 'ecommerce.calculating',
78
- 'receipt_finalizing',
79
- 'ecommerce.calculating_shipping',
80
- 'global_search.placeholder',
81
- 'signing_in_pending',
82
- 'signing_up_pending',
83
- 'reviews.submitting',
84
- 'ecommerce.contact_seller_sending',
85
- 'thread.sending'
86
- )
87
- AND EXISTS (
88
- SELECT 1
89
- FROM jsonb_each_text(t.translations) AS entry(key, value)
90
- WHERE entry.value ~ '[.]{3}$'
91
- );
92
-
93
- -- 3. 122 new keys, English and French. Until this runs the components fall back to the
94
- -- same copy built in (useLabel in the app, translateOrFallback in the shop), which is also
95
- -- what keeps an install that has not migrated yet from showing a raw key.
96
- INSERT INTO public.translations (key, translations, created_at, updated_at)
97
- VALUES
98
- ('account_menu', '{"en":"Account menu","fr":"Menu du compte"}'::jsonb, now(), now()),
99
- ('checkout_success_confirming', '{"en":"Confirming your order…","fr":"Confirmation de votre commande…"}'::jsonb, now(), now()),
100
- ('checkout_success_no_session', '{"en":"No order to show","fr":"Aucune commande à afficher"}'::jsonb, now(), now()),
101
- ('checkout_success_unconfirmed', '{"en":"We could not confirm your order","fr":"Nous n''avons pas pu confirmer votre commande"}'::jsonb, now(), now()),
102
- ('close', '{"en":"Close","fr":"Fermer"}'::jsonb, now(), now()),
103
- ('close_main_menu', '{"en":"Close main menu","fr":"Fermer le menu principal"}'::jsonb, now(), now()),
104
- ('comments.cancel', '{"en":"Cancel","fr":"Annuler"}'::jsonb, now(), now()),
105
- ('comments.submitting', '{"en":"Submitting…","fr":"Envoi en cours…"}'::jsonb, now(), now()),
106
- ('ecommerce.actions', '{"en":"Actions","fr":"Actions"}'::jsonb, now(), now()),
107
- ('ecommerce.apply', '{"en":"Apply","fr":"Appliquer"}'::jsonb, now(), now()),
108
- ('ecommerce.applying', '{"en":"Applying…","fr":"Application…"}'::jsonb, now(), now()),
109
- ('ecommerce.billing_cycle', '{"en":"Billing cycle","fr":"Cycle de facturation"}'::jsonb, now(), now()),
110
- ('ecommerce.cart_error_license_in_cart', '{"en":"This software license is already in your cart.","fr":"Cette licence logicielle est déjà dans votre panier."}'::jsonb, now(), now()),
111
- ('ecommerce.cart_error_out_of_stock', '{"en":"This item is out of stock.","fr":"Cet article est en rupture de stock."}'::jsonb, now(), now()),
112
- ('ecommerce.cart_error_stock_limit', '{"en":"Only {count} available.","fr":"Seulement {count} en stock."}'::jsonb, now(), now()),
113
- ('ecommerce.coupon', '{"en":"Coupon","fr":"Code promo"}'::jsonb, now(), now()),
114
- ('ecommerce.coupon_cart_empty', '{"en":"Add an item to your cart before applying a coupon.","fr":"Ajoutez un article à votre panier avant d''appliquer un code promo."}'::jsonb, now(), now()),
115
- ('ecommerce.coupon_code_required', '{"en":"Enter a coupon code.","fr":"Entrez un code promo."}'::jsonb, now(), now()),
116
- ('ecommerce.coupon_expired', '{"en":"This coupon has expired.","fr":"Ce code promo a expiré."}'::jsonb, now(), now()),
117
- ('ecommerce.coupon_inactive', '{"en":"This coupon is not active.","fr":"Ce code promo n''est pas actif."}'::jsonb, now(), now()),
118
- ('ecommerce.coupon_limit_reached', '{"en":"This coupon has reached its redemption limit.","fr":"Ce code promo a atteint sa limite d''utilisation."}'::jsonb, now(), now()),
119
- ('ecommerce.coupon_not_applicable', '{"en":"This coupon does not apply to the items in your cart.","fr":"Ce code promo ne s''applique pas aux articles de votre panier."}'::jsonb, now(), now()),
120
- ('ecommerce.coupon_not_found', '{"en":"Coupon code not found.","fr":"Code promo introuvable."}'::jsonb, now(), now()),
121
- ('ecommerce.coupon_not_started', '{"en":"This coupon is not active yet.","fr":"Ce code promo n''est pas encore actif."}'::jsonb, now(), now()),
122
- ('ecommerce.coupon_placeholder', '{"en":"SAVE10…","fr":"RABAIS10…"}'::jsonb, now(), now()),
123
- ('ecommerce.coupon_validation_failed', '{"en":"We couldn''t validate this coupon. Try again.","fr":"Impossible de valider ce code promo. Réessayez."}'::jsonb, now(), now()),
124
- ('ecommerce.currency', '{"en":"Currency","fr":"Devise"}'::jsonb, now(), now()),
125
- ('ecommerce.decrease_quantity', '{"en":"Decrease quantity","fr":"Diminuer la quantité"}'::jsonb, now(), now()),
126
- ('ecommerce.discount', '{"en":"Discount","fr":"Rabais"}'::jsonb, now(), now()),
127
- ('ecommerce.gallery_show_image', '{"en":"Show image {index} of {total}","fr":"Afficher l''image {index} sur {total}"}'::jsonb, now(), now()),
128
- ('ecommerce.in_stock_untracked', '{"en":"In stock","fr":"En stock"}'::jsonb, now(), now()),
129
- ('ecommerce.increase_quantity', '{"en":"Increase quantity","fr":"Augmenter la quantité"}'::jsonb, now(), now()),
130
- ('ecommerce.license_copied', '{"en":"Copied","fr":"Copié"}'::jsonb, now(), now()),
131
- ('ecommerce.license_copy', '{"en":"Copy key","fr":"Copier la clé"}'::jsonb, now(), now()),
132
- ('ecommerce.license_expires', '{"en":"Expires","fr":"Expire le"}'::jsonb, now(), now()),
133
- ('ecommerce.license_number', '{"en":"License #","fr":"Nº de licence"}'::jsonb, now(), now()),
134
- ('ecommerce.license_panel_email_fallback', '{"en":"Your license key was sent to your email. It will also appear here once your purchase is confirmed.","fr":"Votre clé de licence vous a été envoyée par courriel. Elle apparaîtra aussi ici une fois votre achat confirmé."}'::jsonb, now(), now()),
135
- ('ecommerce.license_panel_help', '{"en":"Activate this key in your NextBlock install under Settings → Packages.","fr":"Activez cette clé dans votre installation NextBlock, sous Settings → Packages."}'::jsonb, now(), now()),
136
- ('ecommerce.license_panel_title', '{"en":"Your license","fr":"Votre licence"}'::jsonb, now(), now()),
137
- ('ecommerce.license_renews', '{"en":"Renews / expires","fr":"Renouvellement / expiration"}'::jsonb, now(), now()),
138
- ('ecommerce.license_trial_ends', '{"en":"Trial ends","fr":"Fin de l''essai"}'::jsonb, now(), now()),
139
- ('ecommerce.open_cart_count', '{"en":"Open cart ({count} items)","fr":"Ouvrir le panier ({count} articles)"}'::jsonb, now(), now()),
140
- ('ecommerce.rating_out_of_5', '{"en":"Rated {rating} out of 5","fr":"Note de {rating} sur 5"}'::jsonb, now(), now()),
141
- ('ecommerce.regular_price', '{"en":"Regular price","fr":"Prix courant"}'::jsonb, now(), now()),
142
- ('ecommerce.remove_coupon', '{"en":"Remove coupon {code}","fr":"Retirer le code promo {code}"}'::jsonb, now(), now()),
143
- ('ecommerce.remove_item', '{"en":"Remove {item} from cart","fr":"Retirer {item} du panier"}'::jsonb, now(), now()),
144
- ('ecommerce.sale_price', '{"en":"Sale price","fr":"Prix réduit"}'::jsonb, now(), now()),
145
- ('ecommerce.sku', '{"en":"SKU","fr":"UGS"}'::jsonb, now(), now()),
146
- ('ecommerce.start_trial', '{"en":"Start {trial}","fr":"Commencer : {trial}"}'::jsonb, now(), now()),
147
- ('ecommerce.status', '{"en":"Status","fr":"Statut"}'::jsonb, now(), now()),
148
- ('ecommerce.trial_days', '{"en":"{count}-day free trial","fr":"Essai gratuit de {count} jours"}'::jsonb, now(), now()),
149
- ('ecommerce.trial_no_card', '{"en":"No credit card required","fr":"Aucune carte de crédit requise"}'::jsonb, now(), now()),
150
- ('ecommerce.trial_payment_required', '{"en":"Payment method required","fr":"Mode de paiement requis"}'::jsonb, now(), now()),
151
- ('footer_navigation', '{"en":"Footer navigation","fr":"Navigation du pied de page"}'::jsonb, now(), now()),
152
- ('forms.checkbox_default', '{"en":"I agree","fr":"J''accepte"}'::jsonb, now(), now()),
153
- ('forms.error_empty', '{"en":"Please fill in the form before submitting.","fr":"Veuillez remplir le formulaire avant de l''envoyer."}'::jsonb, now(), now()),
154
- ('forms.error_generic', '{"en":"Sorry, there was an error sending your message. Please try again later.","fr":"Désolé, une erreur s''est produite lors de l''envoi de votre message. Veuillez réessayer plus tard."}'::jsonb, now(), now()),
155
- ('forms.error_throttled', '{"en":"You''ve sent several messages already. Please wait a few minutes before sending another.","fr":"Vous avez déjà envoyé plusieurs messages. Veuillez patienter quelques minutes avant d''en envoyer un autre."}'::jsonb, now(), now()),
156
- ('forms.submitting', '{"en":"Submitting…","fr":"Envoi en cours…"}'::jsonb, now(), now()),
157
- ('forms.verification_failed', '{"en":"Security verification could not be completed. Please try again.","fr":"La vérification de sécurité n''a pas pu être complétée. Veuillez réessayer."}'::jsonb, now(), now()),
158
- ('forms.verification_load_failed', '{"en":"Security verification could not be loaded. Please refresh and try again.","fr":"La vérification de sécurité n''a pas pu être chargée. Veuillez actualiser la page et réessayer."}'::jsonb, now(), now()),
159
- ('forms.verification_start_failed', '{"en":"Security verification could not be started. Please try again.","fr":"La vérification de sécurité n''a pas pu démarrer. Veuillez réessayer."}'::jsonb, now(), now()),
160
- ('forms.verification_timeout', '{"en":"Security verification timed out. Please try again.","fr":"La vérification de sécurité a expiré. Veuillez réessayer."}'::jsonb, now(), now()),
161
- ('forms.verifying', '{"en":"Verifying…","fr":"Vérification…"}'::jsonb, now(), now()),
162
- ('global_search.loading', '{"en":"Searching…","fr":"Recherche en cours…"}'::jsonb, now(), now()),
163
- ('global_search.results_count', '{"en":"Results: {count}","fr":"Résultats : {count}"}'::jsonb, now(), now()),
164
- ('interactions.anonymous', '{"en":"Anonymous","fr":"Anonyme"}'::jsonb, now(), now()),
165
- ('language_switcher', '{"en":"Language","fr":"Langue"}'::jsonb, now(), now()),
166
- ('loading', '{"en":"Loading…","fr":"Chargement…"}'::jsonb, now(), now()),
167
- ('main_navigation', '{"en":"Main navigation","fr":"Navigation principale"}'::jsonb, now(), now()),
168
- ('nav.toggle_submenu', '{"en":"Toggle submenu for {label}","fr":"Afficher ou masquer le sous-menu de {label}"}'::jsonb, now(), now()),
169
- ('pagination.label', '{"en":"Pagination","fr":"Pagination"}'::jsonb, now(), now()),
170
- ('pagination.next', '{"en":"Next","fr":"Suivant"}'::jsonb, now(), now()),
171
- ('pagination.page_of', '{"en":"Page {current} of {total}","fr":"Page {current} sur {total}"}'::jsonb, now(), now()),
172
- ('pagination.previous', '{"en":"Previous","fr":"Précédent"}'::jsonb, now(), now()),
173
- ('photo_credit.anonymous', '{"en":"a photographer","fr":"un photographe"}'::jsonb, now(), now()),
174
- ('photo_credit.by', '{"en":"Photo by","fr":"Photo de"}'::jsonb, now(), now()),
175
- ('photo_credit.on', '{"en":"on","fr":"sur"}'::jsonb, now(), now()),
176
- ('posts_grid.empty', '{"en":"No posts found.","fr":"Aucun article trouvé."}'::jsonb, now(), now()),
177
- ('posts_grid.error', '{"en":"Failed to load posts.","fr":"Impossible de charger les articles."}'::jsonb, now(), now()),
178
- ('posts_grid.read_more', '{"en":"Read more","fr":"Lire la suite"}'::jsonb, now(), now()),
179
- ('product_grid.error', '{"en":"Failed to load products.","fr":"Impossible de charger les produits."}'::jsonb, now(), now()),
180
- ('profile', '{"en":"Profile","fr":"Profil"}'::jsonb, now(), now()),
181
- ('profile_change_avatar', '{"en":"Change profile picture","fr":"Changer la photo de profil"}'::jsonb, now(), now()),
182
- ('read_only', '{"en":"read-only","fr":"lecture seule"}'::jsonb, now(), now()),
183
- ('retry', '{"en":"Try again","fr":"Réessayer"}'::jsonb, now(), now()),
184
- ('reviews.rated', '{"en":"Rated {rating} out of 5","fr":"Note : {rating} sur 5"}'::jsonb, now(), now()),
185
- ('reviews.star_one', '{"en":"{count} star","fr":"{count} étoile"}'::jsonb, now(), now()),
186
- ('reviews.star_other', '{"en":"{count} stars","fr":"{count} étoiles"}'::jsonb, now(), now()),
187
- ('skip_to_content', '{"en":"Skip to main content","fr":"Aller au contenu principal"}'::jsonb, now(), now()),
188
- ('slider.go_to', '{"en":"Go to slide {current}","fr":"Aller à la diapositive {current}"}'::jsonb, now(), now()),
189
- ('slider.label', '{"en":"Slideshow","fr":"Diaporama"}'::jsonb, now(), now()),
190
- ('slider.next', '{"en":"Next slide","fr":"Diapositive suivante"}'::jsonb, now(), now()),
191
- ('slider.pause', '{"en":"Pause slideshow","fr":"Mettre le diaporama en pause"}'::jsonb, now(), now()),
192
- ('slider.play', '{"en":"Play slideshow","fr":"Lancer le diaporama"}'::jsonb, now(), now()),
193
- ('slider.previous', '{"en":"Previous slide","fr":"Diapositive précédente"}'::jsonb, now(), now()),
194
- ('slider.slide', '{"en":"Slide {current} of {total}","fr":"Diapositive {current} sur {total}"}'::jsonb, now(), now()),
195
- ('two_factor.code_incomplete', '{"en":"Enter the 6-digit code.","fr":"Entrez le code à 6 chiffres."}'::jsonb, now(), now()),
196
- ('two_factor.code_label', '{"en":"Verification code","fr":"Code de vérification"}'::jsonb, now(), now()),
197
- ('two_factor.code_sent_to', '{"en":"Enter the code we sent to {email}.","fr":"Entrez le code envoyé à {email}."}'::jsonb, now(), now()),
198
- ('two_factor.delivery_help', '{"en":"Codes can take a minute to arrive. If you request another, the earlier one still works: enter whichever reaches you first.","fr":"Les codes peuvent prendre une minute à arriver. Si vous en demandez un autre, le précédent reste valide : entrez celui qui vous parvient en premier."}'::jsonb, now(), now()),
199
- ('two_factor.email_help', '{"en":"For your security, enter the 6-digit code sent to {email}.","fr":"Pour votre sécurité, entrez le code à 6 chiffres envoyé à {email}."}'::jsonb, now(), now()),
200
- ('two_factor.resend', '{"en":"Resend code","fr":"Renvoyer le code"}'::jsonb, now(), now()),
201
- ('two_factor.resend_in', '{"en":"Resend available in {seconds}s","fr":"Renvoi possible dans {seconds} s"}'::jsonb, now(), now()),
202
- ('two_factor.send', '{"en":"Send me a code","fr":"Envoyez-moi un code"}'::jsonb, now(), now()),
203
- ('two_factor.send_failed', '{"en":"Could not send a code.","fr":"Impossible d''envoyer un code."}'::jsonb, now(), now()),
204
- ('two_factor.submit', '{"en":"Verify & continue","fr":"Vérifier et continuer"}'::jsonb, now(), now()),
205
- ('two_factor.title', '{"en":"Two-step verification","fr":"Vérification en deux étapes"}'::jsonb, now(), now()),
206
- ('two_factor.totp_help', '{"en":"Enter the 6-digit code from your authenticator app to finish signing in.","fr":"Entrez le code à 6 chiffres de votre application d''authentification pour terminer la connexion."}'::jsonb, now(), now()),
207
- ('two_factor.verification_failed', '{"en":"Verification failed.","fr":"La vérification a échoué."}'::jsonb, now(), now()),
208
- ('two_factor.your_email', '{"en":"your email","fr":"votre adresse courriel"}'::jsonb, now(), now()),
209
- ('unauthorized.contact_admin', '{"en":"Please contact your administrator if you believe this is an error.","fr":"Veuillez communiquer avec votre administrateur si vous croyez qu''il s''agit d''une erreur."}'::jsonb, now(), now()),
210
- ('unauthorized.description', '{"en":"You do not have the necessary permissions to view this page.","fr":"Vous n''avez pas les autorisations nécessaires pour consulter cette page."}'::jsonb, now(), now()),
211
- ('unauthorized.details', '{"en":"Details:","fr":"Détails :"}'::jsonb, now(), now()),
212
- ('unauthorized.error_code', '{"en":"Error code:","fr":"Code d''erreur :"}'::jsonb, now(), now()),
213
- ('unauthorized.go_home', '{"en":"Go to Homepage","fr":"Retour à l''accueil"}'::jsonb, now(), now()),
214
- ('unauthorized.or', '{"en":"OR","fr":"OU"}'::jsonb, now(), now()),
215
- ('unauthorized.requested_path', '{"en":"Requested path:","fr":"Chemin demandé :"}'::jsonb, now(), now()),
216
- ('unauthorized.required_roles', '{"en":"Required role(s):","fr":"Rôle(s) requis :"}'::jsonb, now(), now()),
217
- ('unauthorized.title', '{"en":"Access Denied","fr":"Accès refusé"}'::jsonb, now(), now()),
218
- ('video.play', '{"en":"Play video: {title}","fr":"Lire la vidéo : {title}"}'::jsonb, now(), now()),
219
- ('video.untitled', '{"en":"YouTube video","fr":"Vidéo YouTube"}'::jsonb, now(), now())
220
- ON CONFLICT (key) DO NOTHING;
1
+ -- Public UI copy: typography, French accents, and the keys the accessibility pass needs.
2
+ --
3
+ -- Data only, and safe to re-run (verified: a second run changes nothing).
4
+ -- 1. French corrections match the EXACT seeded text, so edited copy is left alone.
5
+ -- 2. The ellipsis pass changes a trailing three dots into one ellipsis character and
6
+ -- nothing else, in edited copy too.
7
+ -- 3. New keys use ON CONFLICT DO NOTHING.
8
+
9
+ -- 1. French strings seeded without their accents ("Selectionnez", "Parametres", "a l'accueil").
10
+ -- 43 rows. Runs first, against the original text, so receipt_finalizing gets its accent
11
+ -- and its ellipsis in one step.
12
+ UPDATE public.translations AS t
13
+ SET translations = jsonb_set(t.translations, '{fr}', to_jsonb(fix.new_fr)),
14
+ updated_at = now()
15
+ FROM (
16
+ VALUES
17
+ ('ecommerce.variant_availability_help', 'Selectionnez une combinaison pour afficher le prix exact et la disponibilite de la variante.', 'Sélectionnez une combinaison pour afficher le prix exact et la disponibilité de la variante.'),
18
+ ('ecommerce.variant_selection_required', 'Selectionnez une valeur dans chaque liste pour afficher la variante correspondante.', 'Sélectionnez une valeur dans chaque liste pour afficher la variante correspondante.'),
19
+ ('bill_to', 'Facturer a', 'Facturer à'),
20
+ ('ship_to', 'Livrer a', 'Livrer à'),
21
+ ('invoice_settings', 'Parametres de facture', 'Paramètres de facture'),
22
+ ('print_invoice_help', 'Utilisez la boite de dialogue d''impression de votre navigateur pour enregistrer cette facture en PDF.', 'Utilisez la boîte de dialogue d''impression de votre navigateur pour enregistrer cette facture en PDF.'),
23
+ ('return_home', 'Retour a l''accueil', 'Retour à l''accueil'),
24
+ ('receipt_finalizing', 'Finalisation de votre facture et des details du paiement...', 'Finalisation de votre facture et des détails du paiement…'),
25
+ ('receipt_not_ready', 'Votre facture apparaitra ici une fois la synchronisation du paiement terminee.', 'Votre facture apparaîtra ici une fois la synchronisation du paiement terminée.'),
26
+ ('tax_breakdown', 'Detail des taxes', 'Détail des taxes'),
27
+ ('paid_on', 'Paye le', 'Payé le'),
28
+ ('ecommerce.checkout_trial_started', 'Essai demarre', 'Essai démarré'),
29
+ ('select_an_option', 'Selectionnez une option', 'Sélectionnez une option'),
30
+ ('ecommerce.shipping_method_required', 'Veuillez selectionner un mode de livraison avant de continuer.', 'Veuillez sélectionner un mode de livraison avant de continuer.'),
31
+ ('ecommerce.waiting_on_address_info', 'Completez votre adresse de livraison pour voir les options de livraison disponibles.', 'Complétez votre adresse de livraison pour voir les options de livraison disponibles.'),
32
+ ('ecommerce.sandbox_checkout_stripe_description', 'Cette etape simulee represente le paiement Stripe pour les produits physiques.', 'Cette étape simulée représente le paiement Stripe pour les produits physiques.'),
33
+ ('ecommerce.sandbox_checkout_freemius_description', 'Cette etape simulee represente le paiement Freemius pour les produits numeriques.', 'Cette étape simulée représente le paiement Freemius pour les produits numériques.'),
34
+ ('ecommerce.digital_label', 'Numerique', 'Numérique'),
35
+ ('ecommerce.digital_products', 'Produits numeriques', 'Produits numériques'),
36
+ ('ecommerce.estimated_total', 'Total estime', 'Total estimé'),
37
+ ('ecommerce.shipping_taxes_collected_on_stripe', 'La livraison et les taxes sont percues uniquement a l''etape Stripe pour les produits physiques.', 'La livraison et les taxes sont perçues uniquement à l''étape Stripe pour les produits physiques.'),
38
+ ('ecommerce.freemius_checkout_description', 'Les produits numeriques utilisent le flux de paiement Freemius.', 'Les produits numériques utilisent le flux de paiement Freemius.'),
39
+ ('ecommerce.checkout_billing_cycle_lifetime', 'Abonnement a vie', 'Abonnement à vie'),
40
+ ('ecommerce.checkout_digital_product', 'Payer le produit numerique', 'Payer le produit numérique'),
41
+ ('ecommerce.digital_subtotal', 'Sous-total numerique', 'Sous-total numérique'),
42
+ ('ecommerce.freemius_multi_checkout_notice', 'Les licences Freemius se finalisent une a la fois, donc chaque produit numerique a sa propre action de paiement.', 'Les licences Freemius se finalisent une à la fois, donc chaque produit numérique a sa propre action de paiement.'),
43
+ ('ecommerce.freemius_tax_notice', 'Les taxes et la conformite pour les produits numeriques sont gerees dans le paiement Freemius.', 'Les taxes et la conformité pour les produits numériques sont gérées dans le paiement Freemius.'),
44
+ ('checkout_success_sync_failed', 'Nous n''avons pas encore pu finaliser votre facture. Veuillez rafraichir la page sous peu.', 'Nous n''avons pas encore pu finaliser votre facture. Veuillez rafraîchir la page sous peu.'),
45
+ ('ecommerce.shipping_calculation_failed', 'Nous n''avons pas pu calculer la livraison pour le moment. Veuillez reessayer.', 'Nous n''avons pas pu calculer la livraison pour le moment. Veuillez réessayer.'),
46
+ ('ecommerce.checkout_invalid_items', 'Les articles de votre commande n''ont pas pu etre traites.', 'Les articles de votre commande n''ont pas pu être traités.'),
47
+ ('ecommerce.checkout_provider_items_required', 'Chaque etape de paiement doit inclure des articles associes a un fournisseur de paiement.', 'Chaque étape de paiement doit inclure des articles associés à un fournisseur de paiement.'),
48
+ ('ecommerce.checkout_mixed_provider_steps', 'Les produits utilisant differents fournisseurs de paiement doivent etre achetes en etapes separees.', 'Les produits utilisant différents fournisseurs de paiement doivent être achetés en étapes séparées.'),
49
+ ('ecommerce.checkout_freemius_single_item', 'Les produits Freemius doivent etre achetes un a la fois.', 'Les produits Freemius doivent être achetés un à la fois.'),
50
+ ('ecommerce.checkout_internal_server_error', 'Une erreur s''est produite lors de la preparation de votre paiement. Veuillez reessayer.', 'Une erreur s''est produite lors de la préparation de votre paiement. Veuillez réessayer.'),
51
+ ('ecommerce.checkout_missing_session_id', 'Nous n''avons pas trouve de session de paiement a finaliser.', 'Nous n''avons pas trouvé de session de paiement à finaliser.'),
52
+ ('ecommerce.checkout_success_order_not_found', 'Nous n''avons pas trouve la commande liee a ce paiement.', 'Nous n''avons pas trouvé la commande liée à ce paiement.'),
53
+ ('ecommerce.checkout_success_invalid_reference', 'Cette reference de paiement ne peut pas etre finalisee depuis cette page.', 'Cette référence de paiement ne peut pas être finalisée depuis cette page.'),
54
+ ('ecommerce.checkout_success_inventory_update_failed', 'Nous n''avons pas pu mettre a jour l''inventaire pour cette commande.', 'Nous n''avons pas pu mettre à jour l''inventaire pour cette commande.'),
55
+ ('ecommerce.checkout_success_status_update_failed', 'Nous n''avons pas pu mettre a jour le statut de la commande.', 'Nous n''avons pas pu mettre à jour le statut de la commande.'),
56
+ ('global_search.description', 'Rechercher dans les pages, articles et produits publies.', 'Rechercher dans les pages, articles et produits publiés.'),
57
+ ('global_search.recent', 'Recents', 'Récents'),
58
+ ('global_search.error_description', 'Veuillez reessayer dans un instant.', 'Veuillez réessayer dans un instant.'),
59
+ ('global_search.empty_title', 'Aucun resultat trouve.', 'Aucun résultat trouvé.')
60
+ ) AS fix(key, old_fr, new_fr)
61
+ WHERE t.key = fix.key
62
+ AND t.translations->>'fr' = fix.old_fr;
63
+
64
+ -- 2. "Loading..." -> "Loading…" in every language of the 12 rows seeded with three dots
65
+ -- (Web Interface Guidelines: a real ellipsis character in loading and placeholder copy).
66
+ -- The pattern is a character class on purpose: no backslash to lose along the way.
67
+ UPDATE public.translations AS t
68
+ SET translations = (
69
+ SELECT jsonb_object_agg(entry.key, to_jsonb(regexp_replace(entry.value, '[.]{3}$', '…')))
70
+ FROM jsonb_each_text(t.translations) AS entry(key, value)
71
+ ),
72
+ updated_at = now()
73
+ WHERE t.key IN (
74
+ 'saving',
75
+ 'ecommerce.search_products',
76
+ 'ecommerce.processing',
77
+ 'ecommerce.calculating',
78
+ 'receipt_finalizing',
79
+ 'ecommerce.calculating_shipping',
80
+ 'global_search.placeholder',
81
+ 'signing_in_pending',
82
+ 'signing_up_pending',
83
+ 'reviews.submitting',
84
+ 'ecommerce.contact_seller_sending',
85
+ 'thread.sending'
86
+ )
87
+ AND EXISTS (
88
+ SELECT 1
89
+ FROM jsonb_each_text(t.translations) AS entry(key, value)
90
+ WHERE entry.value ~ '[.]{3}$'
91
+ );
92
+
93
+ -- 3. 122 new keys, English and French. Until this runs the components fall back to the
94
+ -- same copy built in (useLabel in the app, translateOrFallback in the shop), which is also
95
+ -- what keeps an install that has not migrated yet from showing a raw key.
96
+ INSERT INTO public.translations (key, translations, created_at, updated_at)
97
+ VALUES
98
+ ('account_menu', '{"en":"Account menu","fr":"Menu du compte"}'::jsonb, now(), now()),
99
+ ('checkout_success_confirming', '{"en":"Confirming your order…","fr":"Confirmation de votre commande…"}'::jsonb, now(), now()),
100
+ ('checkout_success_no_session', '{"en":"No order to show","fr":"Aucune commande à afficher"}'::jsonb, now(), now()),
101
+ ('checkout_success_unconfirmed', '{"en":"We could not confirm your order","fr":"Nous n''avons pas pu confirmer votre commande"}'::jsonb, now(), now()),
102
+ ('close', '{"en":"Close","fr":"Fermer"}'::jsonb, now(), now()),
103
+ ('close_main_menu', '{"en":"Close main menu","fr":"Fermer le menu principal"}'::jsonb, now(), now()),
104
+ ('comments.cancel', '{"en":"Cancel","fr":"Annuler"}'::jsonb, now(), now()),
105
+ ('comments.submitting', '{"en":"Submitting…","fr":"Envoi en cours…"}'::jsonb, now(), now()),
106
+ ('ecommerce.actions', '{"en":"Actions","fr":"Actions"}'::jsonb, now(), now()),
107
+ ('ecommerce.apply', '{"en":"Apply","fr":"Appliquer"}'::jsonb, now(), now()),
108
+ ('ecommerce.applying', '{"en":"Applying…","fr":"Application…"}'::jsonb, now(), now()),
109
+ ('ecommerce.billing_cycle', '{"en":"Billing cycle","fr":"Cycle de facturation"}'::jsonb, now(), now()),
110
+ ('ecommerce.cart_error_license_in_cart', '{"en":"This software license is already in your cart.","fr":"Cette licence logicielle est déjà dans votre panier."}'::jsonb, now(), now()),
111
+ ('ecommerce.cart_error_out_of_stock', '{"en":"This item is out of stock.","fr":"Cet article est en rupture de stock."}'::jsonb, now(), now()),
112
+ ('ecommerce.cart_error_stock_limit', '{"en":"Only {count} available.","fr":"Seulement {count} en stock."}'::jsonb, now(), now()),
113
+ ('ecommerce.coupon', '{"en":"Coupon","fr":"Code promo"}'::jsonb, now(), now()),
114
+ ('ecommerce.coupon_cart_empty', '{"en":"Add an item to your cart before applying a coupon.","fr":"Ajoutez un article à votre panier avant d''appliquer un code promo."}'::jsonb, now(), now()),
115
+ ('ecommerce.coupon_code_required', '{"en":"Enter a coupon code.","fr":"Entrez un code promo."}'::jsonb, now(), now()),
116
+ ('ecommerce.coupon_expired', '{"en":"This coupon has expired.","fr":"Ce code promo a expiré."}'::jsonb, now(), now()),
117
+ ('ecommerce.coupon_inactive', '{"en":"This coupon is not active.","fr":"Ce code promo n''est pas actif."}'::jsonb, now(), now()),
118
+ ('ecommerce.coupon_limit_reached', '{"en":"This coupon has reached its redemption limit.","fr":"Ce code promo a atteint sa limite d''utilisation."}'::jsonb, now(), now()),
119
+ ('ecommerce.coupon_not_applicable', '{"en":"This coupon does not apply to the items in your cart.","fr":"Ce code promo ne s''applique pas aux articles de votre panier."}'::jsonb, now(), now()),
120
+ ('ecommerce.coupon_not_found', '{"en":"Coupon code not found.","fr":"Code promo introuvable."}'::jsonb, now(), now()),
121
+ ('ecommerce.coupon_not_started', '{"en":"This coupon is not active yet.","fr":"Ce code promo n''est pas encore actif."}'::jsonb, now(), now()),
122
+ ('ecommerce.coupon_placeholder', '{"en":"SAVE10…","fr":"RABAIS10…"}'::jsonb, now(), now()),
123
+ ('ecommerce.coupon_validation_failed', '{"en":"We couldn''t validate this coupon. Try again.","fr":"Impossible de valider ce code promo. Réessayez."}'::jsonb, now(), now()),
124
+ ('ecommerce.currency', '{"en":"Currency","fr":"Devise"}'::jsonb, now(), now()),
125
+ ('ecommerce.decrease_quantity', '{"en":"Decrease quantity","fr":"Diminuer la quantité"}'::jsonb, now(), now()),
126
+ ('ecommerce.discount', '{"en":"Discount","fr":"Rabais"}'::jsonb, now(), now()),
127
+ ('ecommerce.gallery_show_image', '{"en":"Show image {index} of {total}","fr":"Afficher l''image {index} sur {total}"}'::jsonb, now(), now()),
128
+ ('ecommerce.in_stock_untracked', '{"en":"In stock","fr":"En stock"}'::jsonb, now(), now()),
129
+ ('ecommerce.increase_quantity', '{"en":"Increase quantity","fr":"Augmenter la quantité"}'::jsonb, now(), now()),
130
+ ('ecommerce.license_copied', '{"en":"Copied","fr":"Copié"}'::jsonb, now(), now()),
131
+ ('ecommerce.license_copy', '{"en":"Copy key","fr":"Copier la clé"}'::jsonb, now(), now()),
132
+ ('ecommerce.license_expires', '{"en":"Expires","fr":"Expire le"}'::jsonb, now(), now()),
133
+ ('ecommerce.license_number', '{"en":"License #","fr":"Nº de licence"}'::jsonb, now(), now()),
134
+ ('ecommerce.license_panel_email_fallback', '{"en":"Your license key was sent to your email. It will also appear here once your purchase is confirmed.","fr":"Votre clé de licence vous a été envoyée par courriel. Elle apparaîtra aussi ici une fois votre achat confirmé."}'::jsonb, now(), now()),
135
+ ('ecommerce.license_panel_help', '{"en":"Activate this key in your NextBlock install under Settings → Packages.","fr":"Activez cette clé dans votre installation NextBlock, sous Settings → Packages."}'::jsonb, now(), now()),
136
+ ('ecommerce.license_panel_title', '{"en":"Your license","fr":"Votre licence"}'::jsonb, now(), now()),
137
+ ('ecommerce.license_renews', '{"en":"Renews / expires","fr":"Renouvellement / expiration"}'::jsonb, now(), now()),
138
+ ('ecommerce.license_trial_ends', '{"en":"Trial ends","fr":"Fin de l''essai"}'::jsonb, now(), now()),
139
+ ('ecommerce.open_cart_count', '{"en":"Open cart ({count} items)","fr":"Ouvrir le panier ({count} articles)"}'::jsonb, now(), now()),
140
+ ('ecommerce.rating_out_of_5', '{"en":"Rated {rating} out of 5","fr":"Note de {rating} sur 5"}'::jsonb, now(), now()),
141
+ ('ecommerce.regular_price', '{"en":"Regular price","fr":"Prix courant"}'::jsonb, now(), now()),
142
+ ('ecommerce.remove_coupon', '{"en":"Remove coupon {code}","fr":"Retirer le code promo {code}"}'::jsonb, now(), now()),
143
+ ('ecommerce.remove_item', '{"en":"Remove {item} from cart","fr":"Retirer {item} du panier"}'::jsonb, now(), now()),
144
+ ('ecommerce.sale_price', '{"en":"Sale price","fr":"Prix réduit"}'::jsonb, now(), now()),
145
+ ('ecommerce.sku', '{"en":"SKU","fr":"UGS"}'::jsonb, now(), now()),
146
+ ('ecommerce.start_trial', '{"en":"Start {trial}","fr":"Commencer : {trial}"}'::jsonb, now(), now()),
147
+ ('ecommerce.status', '{"en":"Status","fr":"Statut"}'::jsonb, now(), now()),
148
+ ('ecommerce.trial_days', '{"en":"{count}-day free trial","fr":"Essai gratuit de {count} jours"}'::jsonb, now(), now()),
149
+ ('ecommerce.trial_no_card', '{"en":"No credit card required","fr":"Aucune carte de crédit requise"}'::jsonb, now(), now()),
150
+ ('ecommerce.trial_payment_required', '{"en":"Payment method required","fr":"Mode de paiement requis"}'::jsonb, now(), now()),
151
+ ('footer_navigation', '{"en":"Footer navigation","fr":"Navigation du pied de page"}'::jsonb, now(), now()),
152
+ ('forms.checkbox_default', '{"en":"I agree","fr":"J''accepte"}'::jsonb, now(), now()),
153
+ ('forms.error_empty', '{"en":"Please fill in the form before submitting.","fr":"Veuillez remplir le formulaire avant de l''envoyer."}'::jsonb, now(), now()),
154
+ ('forms.error_generic', '{"en":"Sorry, there was an error sending your message. Please try again later.","fr":"Désolé, une erreur s''est produite lors de l''envoi de votre message. Veuillez réessayer plus tard."}'::jsonb, now(), now()),
155
+ ('forms.error_throttled', '{"en":"You''ve sent several messages already. Please wait a few minutes before sending another.","fr":"Vous avez déjà envoyé plusieurs messages. Veuillez patienter quelques minutes avant d''en envoyer un autre."}'::jsonb, now(), now()),
156
+ ('forms.submitting', '{"en":"Submitting…","fr":"Envoi en cours…"}'::jsonb, now(), now()),
157
+ ('forms.verification_failed', '{"en":"Security verification could not be completed. Please try again.","fr":"La vérification de sécurité n''a pas pu être complétée. Veuillez réessayer."}'::jsonb, now(), now()),
158
+ ('forms.verification_load_failed', '{"en":"Security verification could not be loaded. Please refresh and try again.","fr":"La vérification de sécurité n''a pas pu être chargée. Veuillez actualiser la page et réessayer."}'::jsonb, now(), now()),
159
+ ('forms.verification_start_failed', '{"en":"Security verification could not be started. Please try again.","fr":"La vérification de sécurité n''a pas pu démarrer. Veuillez réessayer."}'::jsonb, now(), now()),
160
+ ('forms.verification_timeout', '{"en":"Security verification timed out. Please try again.","fr":"La vérification de sécurité a expiré. Veuillez réessayer."}'::jsonb, now(), now()),
161
+ ('forms.verifying', '{"en":"Verifying…","fr":"Vérification…"}'::jsonb, now(), now()),
162
+ ('global_search.loading', '{"en":"Searching…","fr":"Recherche en cours…"}'::jsonb, now(), now()),
163
+ ('global_search.results_count', '{"en":"Results: {count}","fr":"Résultats : {count}"}'::jsonb, now(), now()),
164
+ ('interactions.anonymous', '{"en":"Anonymous","fr":"Anonyme"}'::jsonb, now(), now()),
165
+ ('language_switcher', '{"en":"Language","fr":"Langue"}'::jsonb, now(), now()),
166
+ ('loading', '{"en":"Loading…","fr":"Chargement…"}'::jsonb, now(), now()),
167
+ ('main_navigation', '{"en":"Main navigation","fr":"Navigation principale"}'::jsonb, now(), now()),
168
+ ('nav.toggle_submenu', '{"en":"Toggle submenu for {label}","fr":"Afficher ou masquer le sous-menu de {label}"}'::jsonb, now(), now()),
169
+ ('pagination.label', '{"en":"Pagination","fr":"Pagination"}'::jsonb, now(), now()),
170
+ ('pagination.next', '{"en":"Next","fr":"Suivant"}'::jsonb, now(), now()),
171
+ ('pagination.page_of', '{"en":"Page {current} of {total}","fr":"Page {current} sur {total}"}'::jsonb, now(), now()),
172
+ ('pagination.previous', '{"en":"Previous","fr":"Précédent"}'::jsonb, now(), now()),
173
+ ('photo_credit.anonymous', '{"en":"a photographer","fr":"un photographe"}'::jsonb, now(), now()),
174
+ ('photo_credit.by', '{"en":"Photo by","fr":"Photo de"}'::jsonb, now(), now()),
175
+ ('photo_credit.on', '{"en":"on","fr":"sur"}'::jsonb, now(), now()),
176
+ ('posts_grid.empty', '{"en":"No posts found.","fr":"Aucun article trouvé."}'::jsonb, now(), now()),
177
+ ('posts_grid.error', '{"en":"Failed to load posts.","fr":"Impossible de charger les articles."}'::jsonb, now(), now()),
178
+ ('posts_grid.read_more', '{"en":"Read more","fr":"Lire la suite"}'::jsonb, now(), now()),
179
+ ('product_grid.error', '{"en":"Failed to load products.","fr":"Impossible de charger les produits."}'::jsonb, now(), now()),
180
+ ('profile', '{"en":"Profile","fr":"Profil"}'::jsonb, now(), now()),
181
+ ('profile_change_avatar', '{"en":"Change profile picture","fr":"Changer la photo de profil"}'::jsonb, now(), now()),
182
+ ('read_only', '{"en":"read-only","fr":"lecture seule"}'::jsonb, now(), now()),
183
+ ('retry', '{"en":"Try again","fr":"Réessayer"}'::jsonb, now(), now()),
184
+ ('reviews.rated', '{"en":"Rated {rating} out of 5","fr":"Note : {rating} sur 5"}'::jsonb, now(), now()),
185
+ ('reviews.star_one', '{"en":"{count} star","fr":"{count} étoile"}'::jsonb, now(), now()),
186
+ ('reviews.star_other', '{"en":"{count} stars","fr":"{count} étoiles"}'::jsonb, now(), now()),
187
+ ('skip_to_content', '{"en":"Skip to main content","fr":"Aller au contenu principal"}'::jsonb, now(), now()),
188
+ ('slider.go_to', '{"en":"Go to slide {current}","fr":"Aller à la diapositive {current}"}'::jsonb, now(), now()),
189
+ ('slider.label', '{"en":"Slideshow","fr":"Diaporama"}'::jsonb, now(), now()),
190
+ ('slider.next', '{"en":"Next slide","fr":"Diapositive suivante"}'::jsonb, now(), now()),
191
+ ('slider.pause', '{"en":"Pause slideshow","fr":"Mettre le diaporama en pause"}'::jsonb, now(), now()),
192
+ ('slider.play', '{"en":"Play slideshow","fr":"Lancer le diaporama"}'::jsonb, now(), now()),
193
+ ('slider.previous', '{"en":"Previous slide","fr":"Diapositive précédente"}'::jsonb, now(), now()),
194
+ ('slider.slide', '{"en":"Slide {current} of {total}","fr":"Diapositive {current} sur {total}"}'::jsonb, now(), now()),
195
+ ('two_factor.code_incomplete', '{"en":"Enter the 6-digit code.","fr":"Entrez le code à 6 chiffres."}'::jsonb, now(), now()),
196
+ ('two_factor.code_label', '{"en":"Verification code","fr":"Code de vérification"}'::jsonb, now(), now()),
197
+ ('two_factor.code_sent_to', '{"en":"Enter the code we sent to {email}.","fr":"Entrez le code envoyé à {email}."}'::jsonb, now(), now()),
198
+ ('two_factor.delivery_help', '{"en":"Codes can take a minute to arrive. If you request another, the earlier one still works: enter whichever reaches you first.","fr":"Les codes peuvent prendre une minute à arriver. Si vous en demandez un autre, le précédent reste valide : entrez celui qui vous parvient en premier."}'::jsonb, now(), now()),
199
+ ('two_factor.email_help', '{"en":"For your security, enter the 6-digit code sent to {email}.","fr":"Pour votre sécurité, entrez le code à 6 chiffres envoyé à {email}."}'::jsonb, now(), now()),
200
+ ('two_factor.resend', '{"en":"Resend code","fr":"Renvoyer le code"}'::jsonb, now(), now()),
201
+ ('two_factor.resend_in', '{"en":"Resend available in {seconds}s","fr":"Renvoi possible dans {seconds} s"}'::jsonb, now(), now()),
202
+ ('two_factor.send', '{"en":"Send me a code","fr":"Envoyez-moi un code"}'::jsonb, now(), now()),
203
+ ('two_factor.send_failed', '{"en":"Could not send a code.","fr":"Impossible d''envoyer un code."}'::jsonb, now(), now()),
204
+ ('two_factor.submit', '{"en":"Verify & continue","fr":"Vérifier et continuer"}'::jsonb, now(), now()),
205
+ ('two_factor.title', '{"en":"Two-step verification","fr":"Vérification en deux étapes"}'::jsonb, now(), now()),
206
+ ('two_factor.totp_help', '{"en":"Enter the 6-digit code from your authenticator app to finish signing in.","fr":"Entrez le code à 6 chiffres de votre application d''authentification pour terminer la connexion."}'::jsonb, now(), now()),
207
+ ('two_factor.verification_failed', '{"en":"Verification failed.","fr":"La vérification a échoué."}'::jsonb, now(), now()),
208
+ ('two_factor.your_email', '{"en":"your email","fr":"votre adresse courriel"}'::jsonb, now(), now()),
209
+ ('unauthorized.contact_admin', '{"en":"Please contact your administrator if you believe this is an error.","fr":"Veuillez communiquer avec votre administrateur si vous croyez qu''il s''agit d''une erreur."}'::jsonb, now(), now()),
210
+ ('unauthorized.description', '{"en":"You do not have the necessary permissions to view this page.","fr":"Vous n''avez pas les autorisations nécessaires pour consulter cette page."}'::jsonb, now(), now()),
211
+ ('unauthorized.details', '{"en":"Details:","fr":"Détails :"}'::jsonb, now(), now()),
212
+ ('unauthorized.error_code', '{"en":"Error code:","fr":"Code d''erreur :"}'::jsonb, now(), now()),
213
+ ('unauthorized.go_home', '{"en":"Go to Homepage","fr":"Retour à l''accueil"}'::jsonb, now(), now()),
214
+ ('unauthorized.or', '{"en":"OR","fr":"OU"}'::jsonb, now(), now()),
215
+ ('unauthorized.requested_path', '{"en":"Requested path:","fr":"Chemin demandé :"}'::jsonb, now(), now()),
216
+ ('unauthorized.required_roles', '{"en":"Required role(s):","fr":"Rôle(s) requis :"}'::jsonb, now(), now()),
217
+ ('unauthorized.title', '{"en":"Access Denied","fr":"Accès refusé"}'::jsonb, now(), now()),
218
+ ('video.play', '{"en":"Play video: {title}","fr":"Lire la vidéo : {title}"}'::jsonb, now(), now()),
219
+ ('video.untitled', '{"en":"YouTube video","fr":"Vidéo YouTube"}'::jsonb, now(), now())
220
+ ON CONFLICT (key) DO NOTHING;
@@ -1,32 +1,32 @@
1
- -- Theme tokens that failed WCAG AA on the seeded palettes (measured against the seeded pairs):
2
- -- dark ring 224 76% 48% -> 213 94% 68% focus ring 2.7:1 on a card -> 7.1:1 (8.1:1 on the page)
3
- -- vibrant primary 320 100% 55% -> 320 100% 42% white button text 3.5:1 -> 4.9:1
4
- -- vibrant destructive 0 100% 50% -> 0 100% 42% white button text 4.0:1 -> 5.4:1
5
- -- light destructive 0 84.2% 60.2% -> 0 72.2% 50.6% light button text 3.6:1 -> 4.6:1
6
- -- Each update is guarded by the seeded value, so a theme an operator has already recoloured in
7
- -- the CMS theme editor is left exactly as they set it. libs/ui/src/styles/theme.css carries the
8
- -- same values for standalone consumers of the UI package.
9
-
10
- UPDATE public.site_themes
11
- SET tokens = tokens || '{"ring": "213 94% 68%"}'::jsonb,
12
- updated_at = now()
13
- WHERE slug = 'dark'
14
- AND tokens->>'ring' = '224 76% 48%';
15
-
16
- UPDATE public.site_themes
17
- SET tokens = tokens || '{"primary": "320 100% 42%"}'::jsonb,
18
- updated_at = now()
19
- WHERE slug = 'vibrant'
20
- AND tokens->>'primary' = '320 100% 55%';
21
-
22
- UPDATE public.site_themes
23
- SET tokens = tokens || '{"destructive": "0 100% 42%"}'::jsonb,
24
- updated_at = now()
25
- WHERE slug = 'vibrant'
26
- AND tokens->>'destructive' = '0 100% 50%';
27
-
28
- UPDATE public.site_themes
29
- SET tokens = tokens || '{"destructive": "0 72.2% 50.6%"}'::jsonb,
30
- updated_at = now()
31
- WHERE slug = 'light'
32
- AND tokens->>'destructive' = '0 84.2% 60.2%';
1
+ -- Theme tokens that failed WCAG AA on the seeded palettes (measured against the seeded pairs):
2
+ -- dark ring 224 76% 48% -> 213 94% 68% focus ring 2.7:1 on a card -> 7.1:1 (8.1:1 on the page)
3
+ -- vibrant primary 320 100% 55% -> 320 100% 42% white button text 3.5:1 -> 4.9:1
4
+ -- vibrant destructive 0 100% 50% -> 0 100% 42% white button text 4.0:1 -> 5.4:1
5
+ -- light destructive 0 84.2% 60.2% -> 0 72.2% 50.6% light button text 3.6:1 -> 4.6:1
6
+ -- Each update is guarded by the seeded value, so a theme an operator has already recoloured in
7
+ -- the CMS theme editor is left exactly as they set it. libs/ui/src/styles/theme.css carries the
8
+ -- same values for standalone consumers of the UI package.
9
+
10
+ UPDATE public.site_themes
11
+ SET tokens = tokens || '{"ring": "213 94% 68%"}'::jsonb,
12
+ updated_at = now()
13
+ WHERE slug = 'dark'
14
+ AND tokens->>'ring' = '224 76% 48%';
15
+
16
+ UPDATE public.site_themes
17
+ SET tokens = tokens || '{"primary": "320 100% 42%"}'::jsonb,
18
+ updated_at = now()
19
+ WHERE slug = 'vibrant'
20
+ AND tokens->>'primary' = '320 100% 55%';
21
+
22
+ UPDATE public.site_themes
23
+ SET tokens = tokens || '{"destructive": "0 100% 42%"}'::jsonb,
24
+ updated_at = now()
25
+ WHERE slug = 'vibrant'
26
+ AND tokens->>'destructive' = '0 100% 50%';
27
+
28
+ UPDATE public.site_themes
29
+ SET tokens = tokens || '{"destructive": "0 72.2% 50.6%"}'::jsonb,
30
+ updated_at = now()
31
+ WHERE slug = 'light'
32
+ AND tokens->>'destructive' = '0 84.2% 60.2%';