Skip to content

Commit 2ccbf06

Browse files
committed
Add i18n support with language switching and RTL layout capabilities
1 parent 6604760 commit 2ccbf06

File tree

9 files changed

+819
-94
lines changed

9 files changed

+819
-94
lines changed

public/i18n/en.json

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,18 @@
3838
"delete_account_confirm": "Are you sure you want to delete your account? This action cannot be undone.",
3939
"delete_account": "Delete Account",
4040
"delete": "Delete",
41-
"cancel": "Cancel"
41+
"cancel": "Cancel",
42+
"no_account": "Don't have an account?",
43+
"to_get_started": "to get started.",
44+
"forgot_password": "Forgot your password?",
45+
"reset_password": "Reset Password",
46+
"first_time_login": "If this is your first time logging in after the system update, use the default password:",
47+
"password_change_prompt": "You'll be prompted to change your password after login.",
48+
"check_auth_status": "Check Auth Status",
49+
"create_account": "Create an Account",
50+
"already_have_account": "Already have an account?",
51+
"instead": "instead.",
52+
"register_subscribe": "Register & Subscribe"
4253
},
4354
"subscription": {
4455
"active_subscription_required": "You need an active subscription to access the dashboard.",
@@ -50,7 +61,19 @@
5061
"copy": "Copy",
5162
"copied": "Copied!",
5263
"waiting_payment": "Waiting for payment confirmation...",
53-
"payment_received": "Payment received! Redirecting to your dashboard..."
64+
"payment_received": "Payment received! Redirecting to your dashboard...",
65+
"plan": "Subscription Plan",
66+
"monthly": "Monthly",
67+
"monthly_price": "$5/month",
68+
"monthly_description": "Billed monthly, cancel anytime",
69+
"yearly": "Yearly",
70+
"yearly_price": "$30/year",
71+
"yearly_description": "Save $30 with annual billing",
72+
"payment_method": "Payment Method",
73+
"bitcoin": "Bitcoin",
74+
"ethereum": "Ethereum",
75+
"solana": "Solana",
76+
"usdc": "USDC"
5477
},
5578
"common": {
5679
"save": "Save",
@@ -66,6 +89,22 @@
6689
"back": "Back",
6790
"next": "Next",
6891
"continue": "Continue",
69-
"check_status": "Check Payment Status"
92+
"check_status": "Check Payment Status",
93+
"note": "Note"
94+
},
95+
"footer": {
96+
"home": "Home",
97+
"api_docs": "API Documentation",
98+
"pricing": "Pricing",
99+
"resources": "Resources",
100+
"api_keys": "API Keys",
101+
"github": "GitHub",
102+
"support": "Support",
103+
"legal": "Legal",
104+
"terms": "Terms of Service",
105+
"privacy": "Privacy Policy",
106+
"refund": "Refund Policy",
107+
"company_name": "Profullstack, Inc.",
108+
"all_rights_reserved": "All rights reserved."
70109
}
71110
}

public/i18n/fr.json

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,18 @@
3838
"delete_account_confirm": "Êtes-vous sûr de vouloir supprimer votre compte ? Cette action ne peut pas être annulée.",
3939
"delete_account": "Supprimer le compte",
4040
"delete": "Supprimer",
41-
"cancel": "Annuler"
41+
"cancel": "Annuler",
42+
"no_account": "Vous n'avez pas de compte ?",
43+
"to_get_started": "pour commencer.",
44+
"forgot_password": "Mot de passe oublié ?",
45+
"reset_password": "Réinitialiser le mot de passe",
46+
"first_time_login": "Si c'est votre première connexion après la mise à jour du système, utilisez le mot de passe par défaut :",
47+
"password_change_prompt": "Vous serez invité à changer votre mot de passe après la connexion.",
48+
"check_auth_status": "Vérifier le statut d'authentification",
49+
"create_account": "Créer un compte",
50+
"already_have_account": "Vous avez déjà un compte ?",
51+
"instead": "à la place.",
52+
"register_subscribe": "S'inscrire et s'abonner"
4253
},
4354
"subscription": {
4455
"active_subscription_required": "Vous avez besoin d'un abonnement actif pour accéder au tableau de bord.",
@@ -50,7 +61,19 @@
5061
"copy": "Copier",
5162
"copied": "Copié !",
5263
"waiting_payment": "En attente de confirmation du paiement...",
53-
"payment_received": "Paiement reçu ! Redirection vers votre tableau de bord..."
64+
"payment_received": "Paiement reçu ! Redirection vers votre tableau de bord...",
65+
"plan": "Plan d'abonnement",
66+
"monthly": "Mensuel",
67+
"monthly_price": "5$/mois",
68+
"monthly_description": "Facturation mensuelle, annulation à tout moment",
69+
"yearly": "Annuel",
70+
"yearly_price": "30$/an",
71+
"yearly_description": "Économisez 30$ avec la facturation annuelle",
72+
"payment_method": "Méthode de paiement",
73+
"bitcoin": "Bitcoin",
74+
"ethereum": "Ethereum",
75+
"solana": "Solana",
76+
"usdc": "USDC"
5477
},
5578
"common": {
5679
"save": "Enregistrer",
@@ -66,6 +89,7 @@
6689
"back": "Retour",
6790
"next": "Suivant",
6891
"continue": "Continuer",
69-
"check_status": "Vérifier l'état du paiement"
92+
"check_status": "Vérifier l'état du paiement",
93+
"note": "Remarque"
7094
}
7195
}

public/js/components/language-switcher.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,22 @@ class LanguageSwitcher extends HTMLElement {
1919
}
2020

2121
// Listen for language changes
22-
window.addEventListener('language-changed', () => this.updateSelectedLanguage());
22+
window.addEventListener('language-changed', (event) => {
23+
console.log(`Language-switcher detected language change to: ${event.detail.language}`);
24+
this.updateSelectedLanguage();
25+
});
26+
27+
// Listen for pre-navigation event to update language before transition starts
28+
document.addEventListener('pre-navigation', () => {
29+
console.log('Language-switcher detected pre-navigation event, updating selected language');
30+
this.updateSelectedLanguage();
31+
});
32+
33+
// Also listen for router transitions to ensure language is preserved
34+
document.addEventListener('spa-transition-end', () => {
35+
console.log('Language-switcher detected route transition, updating selected language');
36+
this.updateSelectedLanguage();
37+
});
2338
}
2439

2540
render() {
@@ -233,6 +248,7 @@ class LanguageSwitcher extends HTMLElement {
233248

234249
updateSelectedLanguage() {
235250
const currentLang = localizer.getLanguage();
251+
console.log(`Updating language-switcher UI to reflect current language: ${currentLang}`);
236252

237253
// Update dropdown button text
238254
const currentLanguageSpan = this.shadowRoot.querySelector('.current-language');
@@ -250,6 +266,11 @@ class LanguageSwitcher extends HTMLElement {
250266
item.classList.remove('active');
251267
}
252268
});
269+
270+
// Force translation application
271+
if (window.app && window.app.translatePage) {
272+
window.app.translatePage();
273+
}
253274
}
254275

255276
getLanguageName(langCode) {

public/js/components/pf-footer.js

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,33 @@ class PfFooter extends HTMLElement {
99

1010
connectedCallback() {
1111
this.render();
12+
this.initEventListeners();
13+
}
14+
15+
initEventListeners() {
16+
// Listen for language changes
17+
window.addEventListener('language-changed', () => {
18+
console.log('pf-footer: Language changed event received');
19+
this.translateElements();
20+
});
21+
22+
// Listen for i18n ready event
23+
window.addEventListener('i18n-ready', () => {
24+
console.log('pf-footer: i18n ready event received');
25+
this.translateElements();
26+
});
27+
28+
// Listen for pre-navigation event to translate before transition starts
29+
document.addEventListener('pre-navigation', () => {
30+
console.log('pf-footer: Pre-navigation event received');
31+
this.translateElements();
32+
});
33+
34+
// Listen for router transitions
35+
document.addEventListener('spa-transition-end', () => {
36+
console.log('pf-footer: SPA transition end event received');
37+
this.translateElements();
38+
});
1239
}
1340

1441
render() {
@@ -83,38 +110,80 @@ class PfFooter extends HTMLElement {
83110
<div class="footer">
84111
<div class="footer-content">
85112
<div class="footer-section">
86-
<h3>convert2doc</h3>
113+
<h3 data-i18n="app_name">convert2doc</h3>
87114
<ul class="footer-links">
88-
<li><a href="/">Home</a></li>
89-
<li><a href="/api-docs">API Documentation</a></li>
90-
<li><a href="/subscription">Pricing</a></li>
115+
<li><a href="/" data-i18n="footer.home">Home</a></li>
116+
<li><a href="/api-docs" data-i18n="footer.api_docs">API Documentation</a></li>
117+
<li><a href="/subscription" data-i18n="footer.pricing">Pricing</a></li>
91118
</ul>
92119
</div>
93120
94121
<div class="footer-section">
95-
<h3>Resources</h3>
122+
<h3 data-i18n="footer.resources">Resources</h3>
96123
<ul class="footer-links">
97-
<li><a href="/api-keys">API Keys</a></li>
98-
<li><a href="https://github.com/profullstack/pdf" target="_blank">GitHub</a></li>
99-
<li><a href="mailto:[email protected]">Support</a></li>
124+
<li><a href="/api-keys" data-i18n="footer.api_keys">API Keys</a></li>
125+
<li><a href="https://github.com/profullstack/pdf" target="_blank" data-i18n="footer.github">GitHub</a></li>
126+
<li><a href="mailto:[email protected]" data-i18n="footer.support">Support</a></li>
100127
</ul>
101128
</div>
102129
103130
<div class="footer-section">
104-
<h3>Legal</h3>
131+
<h3 data-i18n="footer.legal">Legal</h3>
105132
<ul class="footer-links">
106-
<li><a href="/terms">Terms of Service</a></li>
107-
<li><a href="/privacy">Privacy Policy</a></li>
108-
<li><a href="/refund">Refund Policy</a></li>
133+
<li><a href="/terms" data-i18n="footer.terms">Terms of Service</a></li>
134+
<li><a href="/privacy" data-i18n="footer.privacy">Privacy Policy</a></li>
135+
<li><a href="/refund" data-i18n="footer.refund">Refund Policy</a></li>
109136
</ul>
110137
</div>
111138
</div>
112139
113140
<div class="copyright">
114-
&copy; ${year} Profullstack, Inc. All rights reserved.
141+
&copy; ${year} <span data-i18n="footer.company_name">Profullstack, Inc.</span> <span data-i18n="footer.all_rights_reserved">All rights reserved.</span>
115142
</div>
116143
</div>
117144
`;
145+
146+
// Translate elements after rendering
147+
this.translateElements();
148+
}
149+
150+
/**
151+
* Translate elements with data-i18n attributes in the shadow DOM
152+
*/
153+
translateElements() {
154+
console.log('pf-footer: Translating elements in shadow DOM');
155+
156+
// Check if there are any elements to translate
157+
const elementsToTranslate = this.shadowRoot.querySelectorAll('[data-i18n]');
158+
if (elementsToTranslate.length === 0) {
159+
console.log('pf-footer: No elements with data-i18n attribute found');
160+
return;
161+
}
162+
163+
// Check if window.app._t is available (faster than importing)
164+
if (window.app && window.app._t) {
165+
console.log('pf-footer: Using window.app._t for translation');
166+
elementsToTranslate.forEach(element => {
167+
const key = element.getAttribute('data-i18n');
168+
const translated = window.app._t(key);
169+
element.textContent = translated;
170+
console.log(`pf-footer: Translated "${key}" to "${translated}"`);
171+
});
172+
return;
173+
}
174+
175+
// Fallback to importing the translation function
176+
console.log('pf-footer: Importing i18n module for translation');
177+
import('../i18n.js').then(({ _t }) => {
178+
elementsToTranslate.forEach(element => {
179+
const key = element.getAttribute('data-i18n');
180+
const translated = _t(key);
181+
element.textContent = translated;
182+
console.log(`pf-footer: Translated "${key}" to "${translated}"`);
183+
});
184+
}).catch(error => {
185+
console.error('Error importing i18n module:', error);
186+
});
118187
}
119188
}
120189

0 commit comments

Comments
 (0)