Skip to content

Commit bf7941d

Browse files
committed
feat: add multi-language support (ES, FR, ZH, AR, DE, JP, BN) and persistence
1 parent 616d824 commit bf7941d

File tree

9 files changed

+939
-57
lines changed

9 files changed

+939
-57
lines changed

v0/src/components/Navbar/User/UserMenu.vue

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
size="x-small"
1414
icon
1515
class="dialogClose"
16-
@click="drawer = false"
16+
v-on:click="drawer = false"
1717
variant="text"
1818
color="white"
1919
>
@@ -23,18 +23,18 @@
2323

2424
<v-list-item
2525
class="list-item-avatar"
26-
:prepend-avatar="authStore.getUserAvatar"
27-
:prepend-icon="
26+
v-bind:prepend-avatar="authStore.getUserAvatar"
27+
v-bind:prepend-icon="
2828
authStore.getUserAvatar === 'default' ? 'mdi-account-circle-outline' : undefined
2929
"
30-
:title="authStore.getUsername"
30+
v-bind:title="authStore.getUsername"
3131
lines="two"
3232
color="white"
3333
></v-list-item>
3434

3535
<v-list-item>
3636
<v-select
37-
:items="availableLocale"
37+
v-bind:items="availableLocale"
3838
label="Locale"
3939
v-model="locale"
4040
density="compact"
@@ -48,19 +48,18 @@
4848

4949
<v-divider class="my-2 bg-white"></v-divider>
5050

51-
<!-- Authentication Section -->
5251
<template v-if="!authStore.getIsLoggedIn">
5352
<v-list density="compact" nav>
5453
<v-list-item
55-
@click.stop="showAuthModal(true)"
54+
v-on:click="showAuthModal(true)"
5655
prepend-icon="mdi-login"
5756
title="Sign In"
5857
value="sign_in"
5958
variant="text"
6059
color="white"
6160
></v-list-item>
6261
<v-list-item
63-
@click.stop="showAuthModal(false)"
62+
v-on:click="showAuthModal(false)"
6463
prepend-icon="mdi-account-plus"
6564
title="Register"
6665
value="register"
@@ -70,43 +69,42 @@
7069
</v-list>
7170
</template>
7271

73-
<!-- User Menu Section -->
7472
<template v-else>
7573
<v-list density="compact" nav>
7674
<v-list-item
77-
@click.stop="dashboard"
75+
v-on:click="dashboard"
7876
prepend-icon="mdi-view-dashboard-outline"
7977
title="Dashboard"
8078
value="dashboard"
8179
variant="text"
8280
color="white"
8381
></v-list-item>
8482
<v-list-item
85-
@click.stop="my_groups"
83+
v-on:click="my_groups"
8684
prepend-icon="mdi-account-group-outline"
8785
title="My Groups"
8886
value="my_groups"
8987
variant="text"
9088
color="white"
9189
></v-list-item>
9290
<v-list-item
93-
@click.stop="notifications"
91+
v-on:click="notifications"
9492
prepend-icon="mdi-bell-outline"
9593
title="Notifications"
9694
value="notifications"
9795
variant="text"
9896
color="white"
9997
>
10098
<template v-if="unreadCount > 0" v-slot:append>
101-
<v-badge :content="unreadCount" color="error"></v-badge>
99+
<v-badge v-bind:content="unreadCount" color="error"></v-badge>
102100
</template>
103101
</v-list-item>
104102
</v-list>
105103

106104
<v-divider class="my-2 bg-white"></v-divider>
107105

108106
<v-list-item
109-
@click.stop="signout"
107+
v-on:click="signout"
110108
prepend-icon="mdi-logout"
111109
title="Logout"
112110
value="logout"
@@ -120,14 +118,14 @@
120118
<v-btn
121119
class="avatar-btn"
122120
variant="text"
123-
@click.stop="drawer = !drawer"
121+
v-on:click="drawer = !drawer"
124122
rounded="xl"
125123
color="white"
126124
>
127125
<v-avatar
128126
v-if="authStore.getUserAvatar !== 'default'"
129127
size="32"
130-
:image="authStore.getUserAvatar"
128+
v-bind:image="authStore.getUserAvatar"
131129
color="white"
132130
></v-avatar>
133131
<v-icon
@@ -141,27 +139,26 @@
141139
</v-main>
142140
</v-layout>
143141

144-
<!-- Authentication Dialog -->
145142
<v-dialog v-model="authModal" max-width="500" persistent>
146143
<v-card class="auth-modal" style="background-color: white">
147144
<v-toolbar color="#43b984">
148145
<v-toolbar-title class="text-white">
149146
{{ isLoginMode ? 'Sign In' : 'Register' }}
150147
</v-toolbar-title>
151148
<v-spacer></v-spacer>
152-
<v-btn icon @click="authModal = false" color="white">
149+
<v-btn icon v-on:click="authModal = false" color="white">
153150
<v-icon>mdi-close</v-icon>
154151
</v-btn>
155152
</v-toolbar>
156153

157154
<v-card-text class="pa-6">
158-
<v-form @submit.prevent="handleAuthSubmit" ref="authForm">
155+
<v-form v-on:submit.prevent="handleAuthSubmit" ref="authForm">
159156
<v-text-field
160157
v-if="!isLoginMode"
161158
v-model="name"
162159
label="Name"
163160
type="text"
164-
:rules="[requiredRule]"
161+
v-bind:rules="[requiredRule]"
165162
variant="outlined"
166163
class="mb-0"
167164
bg-color="#f0eee6"
@@ -171,7 +168,7 @@
171168
v-model="email"
172169
label="Email"
173170
type="email"
174-
:rules="[requiredRule, emailRule]"
171+
v-bind:rules="[requiredRule, emailRule]"
175172
variant="outlined"
176173
class="mb-0"
177174
bg-color="#f0eee6"
@@ -181,7 +178,7 @@
181178
v-model="password"
182179
label="Password"
183180
type="password"
184-
:rules="[requiredRule, passwordRule]"
181+
v-bind:rules="[requiredRule, passwordRule]"
185182
variant="outlined"
186183
class="mb-0"
187184
bg-color="#f0eee6"
@@ -191,8 +188,8 @@
191188
<v-btn
192189
color="#43b984"
193190
type="submit"
194-
:loading="isLoading"
195-
:disabled="isLoading"
191+
v-bind:loading="isLoading"
192+
v-bind:disabled="isLoading"
196193
size="large"
197194
block
198195
class="mb-2"
@@ -202,7 +199,7 @@
202199

203200
<v-btn
204201
variant="text"
205-
@click="toggleAuthMode"
202+
v-on:click="toggleAuthMode"
206203
size="small"
207204
color="#43b984"
208205
>
@@ -214,20 +211,19 @@
214211
</v-card>
215212
</v-dialog>
216213

217-
<!-- Snackbar Notification -->
218214
<v-snackbar
219215
v-model="snackbar.visible"
220-
:color="snackbar.color"
221-
:timeout="3000"
216+
v-bind:color="snackbar.color"
217+
v-bind:timeout="3000"
222218
location="bottom right"
223219
>
224220
{{ snackbar.message }}
225221

226222
<template v-slot:actions>
227223
<v-btn
228224
variant="text"
229-
@click="snackbar.visible = false"
230-
:icon="mdiClose"
225+
v-on:click="snackbar.visible = false"
226+
v-bind:icon="mdiClose"
231227
color="white"
232228
></v-btn>
233229
</template>
@@ -236,12 +232,11 @@
236232
</template>
237233

238234
<script lang="ts" setup>
239-
import { ref } from 'vue'
235+
import { ref, watch, onMounted } from 'vue'
240236
import { useI18n } from 'vue-i18n'
241237
import { availableLocale } from '#/locales/i18n'
242238
import { useAuthStore } from '#/store/authStore'
243239
import { mdiClose } from '@mdi/js'
244-
// import { fetch } from '@tauri-apps/plugin-http' // Uncomment if using Tauri's HTTP plugin
245240
import './User.scss'
246241
247242
const authStore = useAuthStore()
@@ -256,10 +251,43 @@ const isLoading = ref(false)
256251
const authForm = ref()
257252
const unreadCount = ref(0)
258253
254+
/**
255+
* Watcher to handle language changes:
256+
* 1. Saves preference to localStorage for refresh persistence.
257+
* 2. Updates the document lang and direction (RTL support).
258+
*/
259+
watch(locale, function(newLocale) {
260+
localStorage.setItem('locale', newLocale)
261+
document.documentElement.lang = newLocale
262+
263+
if (newLocale === 'ar') {
264+
document.documentElement.dir = 'rtl'
265+
} else {
266+
document.documentElement.dir = 'ltr'
267+
}
268+
})
269+
270+
/**
271+
* Lifecycle hook to restore user's language preference on load.
272+
*/
273+
onMounted(function() {
274+
const savedLocale = localStorage.getItem('locale')
275+
if (savedLocale) {
276+
locale.value = savedLocale
277+
}
278+
279+
document.documentElement.lang = locale.value
280+
if (locale.value === 'ar') {
281+
document.documentElement.dir = 'rtl'
282+
} else {
283+
document.documentElement.dir = 'ltr'
284+
}
285+
})
286+
259287
// Form validation rules
260-
const requiredRule = (v: string) => !!v || 'This field is required'
261-
const emailRule = (v: string) => /.+@.+\..+/.test(v) || 'E-mail must be valid'
262-
const passwordRule = (v: string) => v.length >= 6 || 'Password must be at least 6 characters'
288+
const requiredRule = function(v: string) { return !!v || 'This field is required' }
289+
const emailRule = function(v: string) { return /.+@.+\..+/.test(v) || 'E-mail must be valid' }
290+
const passwordRule = function(v: string) { return v.length >= 6 || 'Password must be at least 6 characters' }
263291
264292
// Snackbar state
265293
const snackbar = ref({
@@ -312,13 +340,11 @@ async function handleAuthSubmit() {
312340
} catch (e) {
313341
errorData = { message: 'An error occurred' }
314342
}
315-
console.error('Authentication failed:', response.status, errorData)
316343
handleLoginError(response.status, errorData)
317344
return
318345
}
319346
320347
const data = await response.json()
321-
console.log('Auth successful:', data)
322348
323349
if (!data.token) {
324350
throw new Error('No token received from server')
@@ -330,8 +356,7 @@ async function handleAuthSubmit() {
330356
'success'
331357
)
332358
authModal.value = false
333-
} catch (error) {
334-
console.error('Authentication error:', error)
359+
} catch (error: any) {
335360
showSnackbar(`Authentication failed: ${error.message}`, 'error')
336361
} finally {
337362
isLoading.value = false
@@ -361,7 +386,7 @@ function showSnackbar(message: string, type: 'success' | 'error' | 'warning' | '
361386
snackbar.value = {
362387
visible: true,
363388
message,
364-
color: type
389+
color: type === 'error' ? '#dc5656' : '#43b984'
365390
}
366391
}
367392
@@ -381,4 +406,4 @@ function signout() {
381406
authStore.signOut()
382407
showSnackbar('You have been logged out', 'info')
383408
}
384-
</script>
409+
</script>

v0/src/locales/bn.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"simulator": {
3-
"save_online": "অনলাইন সংরক্ষণ করুন",
3+
"save_online": "",
44
"save_offline": "অফলাইন সংরক্ষণ করুন",
55
"preview_circuit": "সার্কিট পূর্বরূপ",
66
"export_verilog": "ভেরিলগ রপ্তানি করুন",

0 commit comments

Comments
 (0)