Translate links and buttons in hardcoded french

This commit is contained in:
kbe
2025-09-05 00:15:59 +02:00
parent 61079c8171
commit a0e53325f7
15 changed files with 179 additions and 6534 deletions

View File

@@ -0,0 +1,82 @@
module LucideHelper
# Create a Lucide icon element
#
# @param name [String] The name of the Lucide icon
# @param options [Hash] Additional options
# @option options [String] :class Additional CSS classes
# @option options [String] :size Size class (e.g., 'w-4 h-4', 'w-6 h-6')
# @option options [Hash] :data Additional data attributes
#
# @return [String] HTML string for the icon
#
# Usage:
# lucide_icon('user')
# lucide_icon('check-circle', class: 'text-green-500', size: 'w-5 h-5')
# lucide_icon('menu', data: { action: 'click->header#toggleMenu' })
def lucide_icon(name, options = {})
css_classes = ["lucide-icon"]
css_classes << options[:size] if options[:size]
css_classes << options[:class] if options[:class]
data_attributes = { lucide: name }
data_attributes.merge!(options[:data]) if options[:data]
content_tag :i, "",
class: css_classes.join(" "),
data: data_attributes,
**options.except(:class, :size, :data)
end
# Create a button with a Lucide icon
#
# @param name [String] The name of the Lucide icon
# @param options [Hash] Button options
# @option options [String] :text Button text (optional)
# @option options [String] :class Additional CSS classes for button
# @option options [String] :icon_class Additional CSS classes for icon
# @option options [String] :icon_size Size class for icon
#
# Usage:
# lucide_button('plus', text: 'Add Item', class: 'btn btn-primary')
# lucide_button('trash-2', class: 'btn-danger', data: { confirm: 'Are you sure?' })
def lucide_button(name, options = {})
text = options.delete(:text)
icon_class = options.delete(:icon_class)
icon_size = options.delete(:icon_size) || 'w-4 h-4'
icon = lucide_icon(name, class: icon_class, size: icon_size)
content = if text.present?
safe_join([icon, " ", text])
else
icon
end
content_tag :button, content, options
end
# Create a link with a Lucide icon
#
# @param name [String] The name of the Lucide icon
# @param url [String] The URL for the link
# @param options [Hash] Link options
#
# Usage:
# lucide_link('edit', edit_user_path(user), text: 'Edit')
# lucide_link('external-link', 'https://example.com', text: 'Visit', target: '_blank')
def lucide_link(name, url, options = {})
text = options.delete(:text)
icon_class = options.delete(:icon_class)
icon_size = options.delete(:icon_size) || 'w-4 h-4'
icon = lucide_icon(name, class: icon_class, size: icon_size)
content = if text.present?
safe_join([icon, " ", text])
else
icon
end
link_to content, url, options
end
end

View File

@@ -0,0 +1,68 @@
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="lucide"
export default class extends Controller {
static targets = ["icon"]
connect() {
this.initializeIcons()
// Listen for Turbo navigation events to reinitialize icons
document.addEventListener('turbo:render', this.handleTurboRender.bind(this))
document.addEventListener('turbo:frame-render', this.handleTurboFrameRender.bind(this))
}
disconnect() {
// Clean up event listeners
document.removeEventListener('turbo:render', this.handleTurboRender.bind(this))
document.removeEventListener('turbo:frame-render', this.handleTurboFrameRender.bind(this))
}
// Initialize all Lucide icons in the controller scope
initializeIcons() {
if (typeof lucide !== 'undefined') {
// Initialize icons within this controller's element
lucide.createIcons({
element: this.element
})
} else {
console.warn('Lucide not loaded yet, retrying...')
// Retry after a short delay if Lucide hasn't loaded yet
setTimeout(() => this.initializeIcons(), 100)
}
}
// Method to reinitialize icons after dynamic content changes
reinitialize() {
this.initializeIcons()
}
// Method to create a specific icon programmatically
createIcon(iconName, element = null) {
if (typeof lucide !== 'undefined') {
const targetElement = element || this.element
lucide.createIcons({
element: targetElement,
icons: {
[iconName]: lucide[iconName]
}
})
}
}
// Handle Turbo page renders
handleTurboRender() {
// Small delay to ensure DOM is fully updated
setTimeout(() => this.initializeIcons(), 10)
}
// Handle Turbo frame renders
handleTurboFrameRender(event) {
// Initialize icons within the specific frame that was rendered
if (event.detail && event.detail.newFrame) {
lucide.createIcons({
element: event.detail.newFrame
})
}
}
}

View File

@@ -5,17 +5,17 @@
<img class="mx-auto h-12 w-auto" src="/icon.svg" alt="Aperonight" />
<% end %>
<h2 class="mt-6 text-center text-3xl font-extrabold text-neutral-900">
<%= t('devise.passwords.new.title') %>
Mot de passe oublié ?
</h2>
<p class="mt-2 text-center text-sm text-neutral-600">
<%= t('devise.passwords.new.description') %>
Entrez votre adresse email ci-dessous et nous vous enverrons un lien pour réinitialiser votre mot de passe.
</p>
</div>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: "mt-8 space-y-6" }) do |f| %>
<div>
<%= f.label :email, class: "block text-sm font-medium text-neutral-700" %>
<%= f.label :email, "Adresse Email", class: "block text-sm font-medium text-neutral-700" %>
<div class="mt-1">
<%= f.email_field :email, autofocus: true, autocomplete: "email",
class: "appearance-none block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-400 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm",
@@ -24,7 +24,7 @@
</div>
<div>
<%= f.submit t('devise.passwords.new.submit'),
<%= f.submit "Envoyer le lien de réinitialisation",
class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
</div>
<% end %>
@@ -35,7 +35,7 @@
<div class="w-full border-t border-neutral-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-neutral-50 text-neutral-600"> <%= t('devise.sessions.new.continue_with') %> </span>
<span class="px-2 bg-neutral-50 text-neutral-600">Continuer avec</span>
</div>
</div>

View File

@@ -4,14 +4,9 @@
<%= link_to "/" do %>
<img class="mx-auto h-12 w-auto" src="/icon.svg" alt="Aperonight" />
<% end %>
<h2 class="mt-6 text-center text-3xl font-extrabold text-neutral-900">
<%= t('devise.registrations.new.title') %>
</h2>
<h2 class="mt-6 text-center text-3xl font-extrabold text-neutral-900">Créer un compte</h2>
<p class="mt-2 text-center text-sm text-neutral-600">
<%= t('devise.registrations.new.or') %>
<a href="<%= new_user_session_path %>" class="font-medium text-purple-600 hover:text-purple-500">
<%= t('devise.registrations.new.sign_in_link') %>
</a>
ou <a href="<%= new_user_session_path %>" class="font-medium text-purple-600 hover:text-purple-500">se connecter à votre compte</a>
</p>
</div>
@@ -19,13 +14,13 @@
<div class="space-y-4">
<div>
<%= f.label :email, class: "block text-sm font-medium text-neutral-700" %>
<%= f.label :email, "Adresse email", class: "block text-sm font-medium text-neutral-700" %>
<%= f.email_field :email, autofocus: true, autocomplete: "email",
class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-500 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
</div>
<div>
<%= f.label :password, class: "block text-sm font-medium text-neutral-700" %>
<%= f.label :password, "Mot de passe", class: "block text-sm font-medium text-neutral-700" %>
<% if @minimum_password_length %>
<em class="text-sm text-neutral-500">(<%= t('devise.registrations.new.minimum_password_length', count: @minimum_password_length) %>)</em>
<% end %>
@@ -34,14 +29,14 @@
</div>
<div>
<%= f.label :password_confirmation, class: "block text-sm font-medium text-neutral-700" %>
<%= f.label :password_confirmation, "Confirmation du mot de passe", class: "block text-sm font-medium text-neutral-700" %>
<%= f.password_field :password_confirmation, autocomplete: "new-password",
class: "mt-1 block w-full px-3 py-2 border border-neutral-300 rounded-md shadow-sm placeholder-neutral-500 focus:outline-none focus:ring-purple-500 focus:border-purple-500 sm:text-sm" %>
</div>
</div>
<div class="actions">
<%= f.submit t('devise.registrations.new.sign_up'), class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
<div class="acthons">
<%= f.submit "Créer un compte", class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
</div>
<% end %>
@@ -51,7 +46,7 @@
<div class="w-full border-t border-neutral-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-neutral-50 text-neutral-600"> <%= t('devise.registrations.new.continue_with') %> </span>
<span class="px-2 bg-neutral-50 text-neutral-600">Continuer avec</span>
</div>
</div>

View File

@@ -5,12 +5,12 @@
<img class="mx-auto h-12 w-auto" src="/icon.svg" alt="Aperonight" />
<% end %>
<h2 class="mt-6 text-center text-3xl font-extrabold text-neutral-900">
<%= t('devise.sessions.new.title') %>
Se connecter à votre compte
</h2>
<p class="mt-2 text-center text-sm text-neutral-600">
<%= t('devise.sessions.new.or') %>
ou
<a href="<%= new_user_registration_path %>" class="font-medium text-purple-600 hover:text-purple-500">
<%= t('devise.sessions.new.sign_up_link') %>
créer un compte
</a>
</p>
</div>
@@ -19,17 +19,17 @@
<div class="rounded-md shadow-sm -space-y-px">
<div class="field">
<%= f.label :email, class: "sr-only" %>
<%= f.label :email, "Email", class: "sr-only" %>
<%= f.email_field :email, autofocus: true, autocomplete: "email",
class: "appearance-none rounded-none relative block w-full px-3 py-2 border border-neutral-300 placeholder-neutral-500 text-neutral-900 bg-white rounded-t-md focus:outline-none focus:ring-purple-500 focus:border-purple-500 focus:z-10 sm:text-sm",
placeholder: t('devise.sessions.new.email_placeholder') %>
placeholder: "Adresse email" %>
</div>
<div class="field">
<%= f.label :password, class: "sr-only" %>
<%= f.label :password, "Mot de passe", class: "sr-only" %>
<%= f.password_field :password, autocomplete: "current-password",
class: "appearance-none rounded-none relative block w-full px-3 py-2 border border-neutral-300 placeholder-neutral-500 text-neutral-900 bg-white rounded-b-md focus:outline-none focus:ring-purple-500 focus:border-purple-500 focus:z-10 sm:text-sm",
placeholder: t('devise.sessions.new.password_placeholder') %>
placeholder: "Mot de passe" %>
</div>
</div>
@@ -37,14 +37,14 @@
<div class="flex items-center justify-between">
<div class="flex items-center">
<%= f.check_box :remember_me, class: "h-4 w-4 text-purple-600 focus:ring-purple-500 border-neutral-300 rounded bg-white" %>
<label for="user_remember_me" class="ml-2 block text-sm text-neutral-700"> <%= t('devise.sessions.new.remember_me') %> </label>
<label for="user_remember_me" class="ml-2 block text-sm text-neutral-700"> Se souvenir de moi </label>
</div>
</div>
<% end %>
<div class="actions">
<%= f.submit t('devise.sessions.new.sign_in'), class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
<%= f.submit "Se connecter", class: "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-neutral-50 focus:ring-purple-500" %>
</div>
<% end %>
@@ -54,7 +54,7 @@
<div class="w-full border-t border-neutral-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-neutral-50 text-neutral-600"> <%= t('devise.sessions.new.continue_with') %> </span>
<span class="px-2 bg-neutral-50 text-neutral-600">Continuer avec</span>
</div>
</div>

View File

@@ -1,38 +1,38 @@
<div class="mt-4 space-y-4">
<%- if controller_name != "sessions" %>
<div class="w-full flex justify-center py-2 px-4 border border-neutral-300 rounded-md shadow-sm text-sm font-medium text-purple-600 bg-white hover:bg-neutral-50 hover:border-purple-500 transition-all duration-200">
<%= link_to t('devise.shared.links.sign_in'), new_session_path(resource_name), class: "block" %>
<%= link_to "Se connecter", new_session_path(resource_name), class: "block" %>
</div>
<% end %>
<%- if devise_mapping.registerable? && controller_name != "registrations" %>
<div class="w-full flex justify-center mt-4 py-2 px-4 border border-neutral-300 rounded-md shadow-sm text-sm font-medium text-purple-600 bg-white hover:bg-neutral-50 hover:border-purple-500 transition-all duration-200">
<%= link_to t('devise.shared.links.sign_up'), new_registration_path(resource_name), class: "block" %>
<%= link_to "Créer un compte", new_registration_path(resource_name), class: "block" %>
</div>
<% end %>
<%- if devise_mapping.recoverable? && controller_name != "passwords" && controller_name != "registrations" %>
<div class="w-full flex justify-center mt-4 py-2 px-4 border border-neutral-300 rounded-md shadow-sm text-sm font-medium text-purple-600 bg-white hover:bg-neutral-50 hover:border-purple-500 transition-all duration-200">
<%= link_to t('devise.shared.links.forgot_password'), new_password_path(resource_name), class: "block" %>
<%= link_to "Mot de passe oublié ?", new_password_path(resource_name), class: "block" %>
</div>
<% end %>
<%- if devise_mapping.confirmable? && controller_name != "confirmations" %>
<div class="w-full flex justify-center mt-4 py-2 px-4 border border-neutral-300 rounded-md shadow-sm text-sm font-medium text-purple-600 bg-white hover:bg-neutral-50 hover:border-purple-500 transition-all duration-200">
<%= link_to t('devise.shared.links.confirmation_instructions'), new_confirmation_path(resource_name), class: "block" %>
<%= link_to "Renvoyer le lien de confirmation", new_confirmation_path(resource_name), class: "block" %>
</div>
<% end %>
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != "unlocks" %>
<div class="w-full flex justify-center mt-4 py-2 px-4 border border-neutral-300 rounded-md shadow-sm text-sm font-medium text-purple-600 bg-white hover:bg-neutral-50 hover:border-purple-500 transition-all duration-200">
<%= link_to t('devise.shared.links.unlock_instructions'), new_unlock_path(resource_name), class: "block" %>
<%= link_to "Renvoyer le lien de déblocage", new_unlock_path(resource_name), class: "block" %>
</div>
<% end %>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<div class="w-full flex justify-center mt-4 py-2 px-4 border border-neutral-300 rounded-md shadow-sm text-sm font-medium text-purple-600 bg-white hover:bg-neutral-50 hover:border-purple-500 transition-all duration-200">
<%= button_to t('devise.shared.links.sign_in_with', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider), data: { turbo: false }, class: "block" %>
<%= button_to "Se connecter avec #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false }, class: "block" %>
</div>
<% end %>
<% end %>