Skip to content

Commit 9e72fd6

Browse files
committed
feat(i18n): Integrate i18n across all components with enhancements
- Import and use vue-i18n in all components and views - Add LanguageSwitcher component for runtime language switching - Replace all hardcoded strings with translation keys - Add getChatMessage to electron menu for language switching support - Fix ConfigurationLogsView.vue conflict with upstream monaco editor changes - Use common.* keys for shared translations (cancel, save, reset) - Add JSDoc to all new i18n helper functions
1 parent f7e222b commit 9e72fd6

117 files changed

Lines changed: 4483 additions & 2833 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/App.vue

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@
149149
<script setup lang="ts">
150150
import { useStorage, useWindowSize } from '@vueuse/core'
151151
import { computed, onBeforeMount, onBeforeUnmount, onMounted, ref, watch } from 'vue'
152+
import { useI18n } from 'vue-i18n'
153+
import { useLocale } from 'vuetify'
152154
153155
import ArchitectureWarning from '@/components/ArchitectureWarning.vue'
154156
import ExternalFeaturesDiscoveryModal from '@/components/ExternalFeaturesDiscoveryModal.vue'
@@ -194,6 +196,30 @@ const missionStore = useMissionStore()
194196
// Initialize the snapshot store to register action callbacks
195197
useSnapshotStore()
196198
199+
// Sync Vuetify locale with vue-i18n
200+
const { locale: i18nLocale, t } = useI18n()
201+
const { current: vuetifyLocale } = useLocale()
202+
203+
// Map vue-i18n locales to Vuetify locales
204+
const localeMap: Record<string, string> = {
205+
en: 'en',
206+
zh: 'zhHans',
207+
}
208+
209+
// Watch for i18n locale changes and update Vuetify
210+
watch(
211+
i18nLocale,
212+
(newLocale) => {
213+
vuetifyLocale.value = localeMap[newLocale] || 'en'
214+
215+
// Update Electron menu language if running in Electron
216+
if (window.electronAPI?.updateMenuLanguage) {
217+
window.electronAPI.updateMenuLanguage(newLocale)
218+
}
219+
},
220+
{ immediate: true }
221+
)
222+
197223
const showAboutDialog = ref(false)
198224
const currentSubMenuComponent = ref<SubMenuComponent>(null)
199225
@@ -297,7 +323,7 @@ watch(
297323
(isOnline) => {
298324
if (!isOnline) {
299325
openSnackbar({
300-
message: 'Vehicle connection lost: reestablishing',
326+
message: t('errors.vehicleConnectionLost'),
301327
variant: 'error',
302328
duration: 3000,
303329
closeButton: false,
@@ -308,7 +334,7 @@ watch(
308334
return
309335
}
310336
311-
openSnackbar({ message: 'Vehicle connected', variant: 'success', duration: 3000, closeButton: false })
337+
openSnackbar({ message: t('errors.vehicleConnected'), variant: 'success', duration: 3000, closeButton: false })
312338
connectionStatusFeedback.value = { border: '3px solid green' }
313339
314340
resetConnectionStatusFeedback()

src/components/About.vue

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,24 @@
1616
<div class="w-[90%] flex justify-between my-6 py-3">
1717
<div class="w-[45%] flex flex-col text-start">
1818
<p class="mb-1">
19-
Cockpit is an intuitive and customizable cross-platform ground control station for remote vehicles of
20-
all types.
19+
{{ $t('about.description1') }}
2120
</p>
22-
<p class="my-3">It was created by Blue Robotics and is entirely open-source.</p>
21+
<p class="my-3">{{ $t('about.description2') }}</p>
2322
<p class="mt-1">
24-
It currently supports Ardupilot-based vehicles, but has plans to support any generic vehicle, be it
25-
communicating MAVLink or not.
23+
{{ $t('about.description3') }}
2624
</p>
2725
</div>
2826
<div class="w-[45%] flex flex-col justify-end text-end">
2927
<p class="mb-1">
30-
Version
28+
{{ $t('about.version') }}
3129
<a :href="app_version.link" target="_blank" class="text-primary hover:underline">
3230
{{ app_version.version }}
3331
</a>
3432
<br />
35-
<span class="text-sm text-gray-500">Released: {{ app_version.date }}</span>
33+
<span class="text-sm text-gray-500">{{ $t('about.released') }}: {{ app_version.date }}</span>
3634
</p>
37-
<p class="my-3">Created by Blue Robotics</p>
38-
<p class="mt-1">Licensed under AGPL-3.0-only or LicenseRef-Cockpit-Custom</p>
35+
<p class="my-3">{{ $t('about.createdBy') }}</p>
36+
<p class="mt-1">{{ $t('about.license') }}</p>
3937
</div>
4038
</div>
4139
<div class="mb-5 flex justify-center align-center">
@@ -67,7 +65,9 @@
6765
</div>
6866
</template>
6967
<template #actions
70-
><div class="flex w-full justify-end"><v-btn @click="closeDialog">Close</v-btn></div></template
68+
><div class="flex w-full justify-end">
69+
<v-btn @click="closeDialog">{{ $t('common.close') }}</v-btn>
70+
</div></template
7171
>
7272
</InteractionDialog>
7373
</teleport>

src/components/ArchitectureWarning.vue

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<InteractionDialog
33
v-model="showArchWarningDialog"
4-
title="Performance Warning"
4+
:title="$t('components.ArchitectureWarning.title')"
55
variant="text-only"
66
:actions="dialogActions"
77
max-width="820"
@@ -10,16 +10,14 @@
1010
<div class="flex items-center justify-center mb-2">
1111
<v-icon class="text-yellow text-[60px] mx-8">mdi-alert-rhombus</v-icon>
1212
<div class="flex flex-col font-medium gap-y-3 w-full">
13-
You are running the x64 version of Cockpit on an Apple Silicon Mac (M series), which causes severely degraded
14-
performance, including:
13+
{{ $t('components.ArchitectureWarning.runningWrongVersion') }}
1514
<ul class="mt- ml-4">
16-
<li>- 3-4x slower application startup times</li>
17-
<li>- 2x the memory usage</li>
18-
<li>- Reduced overall performance</li>
15+
<li>- {{ $t('components.ArchitectureWarning.slowerStartup') }}</li>
16+
<li>- {{ $t('components.ArchitectureWarning.doubleMemory') }}</li>
17+
<li>- {{ $t('components.ArchitectureWarning.reducedPerformance') }}</li>
1918
</ul>
2019
<p class="text-sm text-gray-600 mt-2">
21-
This warning cannot be disabled - we strongly recommend that you download and install the intended version
22-
for your system.
20+
{{ $t('components.ArchitectureWarning.cannotDisable') }}
2321
</p>
2422
</div>
2523
</div>
@@ -29,22 +27,24 @@
2927

3028
<script setup lang="ts">
3129
import { onBeforeMount, ref } from 'vue'
30+
import { useI18n } from 'vue-i18n'
3231
3332
import InteractionDialog, { type Action } from '@/components/InteractionDialog.vue'
3433
import { isElectron } from '@/libs/utils'
3534
import { PlatformUtils } from '@/types/platform'
3635
36+
const { t } = useI18n()
3737
const showArchWarningDialog = ref(false)
3838
39-
const dialogActions = [
39+
const dialogActions: Action[] = [
4040
{
41-
text: 'Dismiss',
41+
text: t('components.ArchitectureWarning.dismiss'),
4242
action: () => {
4343
showArchWarningDialog.value = false
4444
},
4545
},
4646
{
47-
text: 'Download ARM64 Version',
47+
text: t('components.ArchitectureWarning.downloadARM'),
4848
action: () => {
4949
window.open('https://github.com/bluerobotics/cockpit/releases/', '_blank')
5050
showArchWarningDialog.value = false

src/components/ArmSafetyDialog.vue

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<template>
2-
<InteractionDialog v-model="show" title="Be careful" variant="text-only" max-width="780px" :persistent="false">
2+
<InteractionDialog
3+
v-model="show"
4+
:title="$t('components.ArmSafetyDialog.title')"
5+
variant="text-only"
6+
max-width="780px"
7+
:persistent="false"
8+
>
39
<template #content>
410
<div class="flex gap-x-2 absolute top-0 right-0 py-2 pr-3">
511
<slot name="help-icon"></slot>
@@ -11,34 +17,42 @@
1117
<div class="flex items-center justify-center mb-6">
1218
<v-icon class="text-yellow text-[60px] mx-8">mdi-alert-rhombus</v-icon>
1319
<p class="w-[560px] text-balance">
14-
The vehicle is currently armed, and the main-menu contains configurations and tools that can cause unsafe
15-
situations.
20+
{{ $t('components.ArmSafetyDialog.vehicleArmedWarning') }}
1621
</p>
17-
<p class="w-[560px] text-balance">Come back later, or proceed carefully with one of the following options:</p>
22+
<p class="w-[560px] text-balance">{{ $t('components.ArmSafetyDialog.proceedCarefully') }}</p>
1823
</div>
1924
</template>
2025
<template #actions>
2126
<div class="flex items-center justify-between gap-8 w-full text-md">
22-
<button class="option-button" @click="neverAskAgain">Continue and never warn again</button>
27+
<button class="option-button" @click="neverAskAgain">
28+
{{ $t('components.ArmSafetyDialog.continueNeverWarn') }}
29+
</button>
2330
<button class="option-button" @click="doNotAskAgainInThisSession">
24-
Continue and don't warn again during this session
31+
{{ $t('components.ArmSafetyDialog.continueSessionWarn') }}
32+
</button>
33+
<button class="option-button" @click="continueAnyway">
34+
{{ $t('components.ArmSafetyDialog.continueAnyway') }}
35+
</button>
36+
<button class="option-button" @click="disarmVehicle">
37+
{{ $t('components.ArmSafetyDialog.disarmAndContinue') }}
2538
</button>
26-
<button class="option-button" @click="continueAnyway">Continue anyway</button>
27-
<button class="option-button" @click="disarmVehicle">Disarm vehicle and continue</button>
2839
</div>
2940
</template>
3041
</InteractionDialog>
3142
</template>
3243

3344
<script setup lang="ts">
3445
import { ref } from 'vue'
46+
import { useI18n } from 'vue-i18n'
3547
3648
import InteractionDialog from '@/components/InteractionDialog.vue'
3749
import { useSnackbar } from '@/composables/snackbar'
3850
import { useAlertStore } from '@/stores/alert'
3951
import { useAppInterfaceStore } from '@/stores/appInterface'
4052
import { useMainVehicleStore } from '@/stores/mainVehicle'
4153
54+
const { t } = useI18n()
55+
4256
const vehicleStore = useMainVehicleStore()
4357
const alertStore = useAlertStore()
4458
const interfaceStore = useAppInterfaceStore()
@@ -65,7 +79,7 @@ const neverAskAgain = (): void => {
6579
continueAnyway()
6680
6781
openSnackbar({
68-
message: 'Armed menu warning disabled. You can re-enable it in the Settings > Alerts menu.',
82+
message: t('components.ArmSafetyDialog.warningDisabled'),
6983
variant: 'info',
7084
duration: 10000,
7185
closeButton: true,

src/components/DataLakeVariableDialog.vue

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
<template>
1+
<template>
22
<v-dialog :model-value="modelValue" max-width="560px" @update:model-value="emit('update:modelValue', $event)">
33
<v-card class="rounded-lg" :style="interfaceStore.globalGlassMenuStyles">
44
<v-card-title class="text-h6 font-weight-bold py-4 text-center">
5-
{{ editMode ? 'Edit Variable' : 'New Variable' }}
5+
{{
6+
editMode
7+
? $t('components.DataLakeVariableDialog.editVariable')
8+
: $t('components.DataLakeVariableDialog.newVariable')
9+
}}
610
</v-card-title>
711
<v-card-text class="px-8">
812
<div class="flex flex-col gap-4">
913
<div class="flex items-center gap-2">
1014
<v-text-field
1115
v-model="variable.id"
12-
label="Variable ID"
16+
:label="$t('components.DataLakeVariableDialog.variableId')"
1317
variant="outlined"
1418
:disabled="editMode || !isManualIdEnabled"
1519
:rules="[(v) => !!v || 'ID is required']"
@@ -29,24 +33,28 @@
2933
</div>
3034
<v-text-field
3135
v-model="variable.name"
32-
label="Variable Name"
36+
:label="$t('components.DataLakeVariableDialog.variableName')"
3337
variant="outlined"
3438
:rules="[(v) => !!v || 'Name is required']"
3539
density="compact"
3640
hide-details
3741
/>
3842
<div class="flex items-center gap-2">
39-
<label class="text-sm">Variable Type: </label>
43+
<label class="text-sm">{{ $t('components.DataLakeVariableDialog.variableType') }}: </label>
4044
<v-radio-group
4145
v-model="variable.type"
4246
:rules="[(v) => !!v || 'Type is required']"
4347
density="compact"
4448
hide-details
4549
inline
4650
>
47-
<v-radio class="ml-3 mr-4" label="String" value="string" />
48-
<v-radio class="ml-3 mr-4" label="Number" value="number" />
49-
<v-radio class="ml-3 mr-4" label="Boolean" value="boolean" />
51+
<v-radio class="ml-3 mr-4" :label="$t('components.DataLakeVariableDialog.types.string')" value="string" />
52+
<v-radio class="ml-3 mr-4" :label="$t('components.DataLakeVariableDialog.types.number')" value="number" />
53+
<v-radio
54+
class="ml-3 mr-4"
55+
:label="$t('components.DataLakeVariableDialog.types.boolean')"
56+
value="boolean"
57+
/>
5058
</v-radio-group>
5159
</div>
5260
<v-text-field
@@ -60,33 +68,33 @@
6068
/>
6169
<v-textarea
6270
v-model="variable.description"
63-
label="Description"
71+
:label="$t('common.description')"
6472
variant="outlined"
65-
placeholder="Optional description of what this variable is used for"
73+
:placeholder="$t('components.DataLakeVariableDialog.descriptionPlaceholder')"
6674
rows="1"
6775
density="compact"
6876
hide-details
6977
/>
7078
<v-checkbox
7179
v-model="variable.persistent"
72-
label="Persist variable between boots"
80+
:label="$t('components.DataLakeVariableDialog.persistBetweenBoots')"
7381
hide-details
7482
class="-mb-4 -mt-2"
7583
/>
7684
<v-checkbox
7785
v-model="variable.persistValue"
7886
class="-my-4"
7987
hide-details
80-
label="Save variable value between boots"
88+
:label="$t('components.DataLakeVariableDialog.saveValueBetweenBoots')"
8189
:disabled="!variable.persistent"
8290
/>
8391
</div>
8492
</v-card-text>
8593
<v-divider class="mx-10" />
8694
<v-card-actions>
8795
<div class="flex justify-between items-center pa-2 w-full h-full">
88-
<v-btn color="white" variant="text" @click="closeDialog">Cancel</v-btn>
89-
<v-btn color="white" :disabled="!isValid" @click="saveVariable">Save</v-btn>
96+
<v-btn color="white" variant="text" @click="closeDialog">{{ $t('common.cancel') }}</v-btn>
97+
<v-btn color="white" :disabled="!isValid" @click="saveVariable">{{ $t('common.save') }}</v-btn>
9098
</div>
9199
</v-card-actions>
92100
</v-card>

0 commit comments

Comments
 (0)