Skip to content

Commit 2bc9053

Browse files
committed
feat(i18n): Complete joystick configuration internationalization
- Add 80+ translation keys for joystick configuration UI - Implement dynamic translation functions with ID-based lookup - Update protocol files for dynamic translations: - cockpit-actions.ts: Dynamic getCockpitActions() getter - other.ts: Dynamic getOtherAvailableActions() and getModifierKeyActions() getters - predefined-resources.ts: Internationalized camera control variable names - protocols.ts: Updated to use dynamic translation getters - Add GPS fix type translation in SatelliteIndicator component - Complete JoystickCalibration dialog internationalization - Add custom v-select slot templates for dropdown translations - Update Chinese translations (EXPO terminology: '指数缩放' 'EXPO缩放') - Ensure all joystick UI elements are localized
1 parent ffaf4a4 commit 2bc9053

10 files changed

Lines changed: 379 additions & 120 deletions

File tree

src/components/joysticks/JoystickCalibration.vue

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
<div class="flex items-center justify-between gap-8">
66
<div class="flex items-center gap-1">
77
<v-checkbox v-model="currentCalibration.deadband.enabled" density="compact" hide-details class="mt-0" />
8-
<span>Deadband/Deadzone</span>
8+
<span>{{ $t('configuration.joystick.deadbandDeadzone') }}</span>
99
</div>
1010
<div class="flex items-center gap-1">
1111
<v-checkbox v-model="currentCalibration.exponential.enabled" density="compact" hide-details class="mt-0" />
12-
<span>Exponential Scaling</span>
12+
<span>{{ $t('configuration.joystick.exponentialScaling') }}</span>
1313
</div>
1414
</div>
15-
<v-btn variant="text" class="text-blue-400" @click="openCalibrationModal"> Calibrate </v-btn>
15+
<v-btn variant="text" class="text-blue-400" @click="openCalibrationModal">{{ $t('configuration.joystick.calibrate') }}</v-btn>
1616
</div>
1717
</div>
1818
</div>
@@ -21,7 +21,7 @@
2121
<InteractionDialog v-model="showCalibrationModal" max-width="1000px" variant="text-only" persistent>
2222
<template #title>
2323
<div class="flex justify-center w-full font-bold mt-1 relative">
24-
Joystick Calibration
24+
{{ $t('configuration.joystick.joystickCalibration') }}
2525
<v-icon class="absolute right-2 top-1 cursor-pointer" size="24" @click="showInstructions = !showInstructions">
2626
mdi-information-outline
2727
</v-icon>
@@ -33,30 +33,22 @@
3333
<v-expand-transition>
3434
<div v-if="showInstructions" class="help-panel mb-4 p-4 rounded bg-white/5 w-full">
3535
<div class="mb-1">
36-
<div class="font-semibold text-base mb-1">Deadband Calibration</div>
36+
<div class="font-semibold text-base mb-1">{{ $t('configuration.joystick.deadbandCalibration') }}</div>
3737
<p class="text-xs text-gray-400 mb-1">
38-
This allows ignoring small unwanted movements near the center of each joystick.
38+
{{ $t('configuration.joystick.deadbandDescription1') }}
3939
</p>
4040
<p class="text-xs text-gray-400 mb-1">
41-
The deadband regions are the red regions in the center of each graph, specifying how much of the
42-
joystick axis range to ignore. Within each deadband the output is clamped to 0, and the output curve
43-
(linear or exponential) starts at the edges of the region. This is useful if a spring-loaded joystick
44-
does not get consistently returned to the exact center of each axis.
41+
{{ $t('configuration.joystick.deadbandDescription2') }}
4542
</p>
4643
<p class="text-xs text-gray-400 mb-2">
47-
To calibrate the deadband, click and drag the deadband regions on the graphs, set the region width
48-
numbers directly with the text-input, or click the "auto calibrate deadband" button at the bottom. If
49-
auto-calibrating, gently touch the sticks during the calibration, while trying not to actually move
50-
them.
44+
{{ $t('configuration.joystick.deadbandDescription3') }}
5145
</p>
5246
</div>
5347
<div>
54-
<div class="font-semibold text-base mb-1">Exponential Calibration</div>
55-
<p class="text-xs text-gray-400 mb-1">This adjusts the sensitivity curve of your joysticks.</p>
48+
<div class="font-semibold text-base mb-1">{{ $t('configuration.joystick.exponentialCalibration') }}</div>
49+
<p class="text-xs text-gray-400 mb-1">{{ $t('configuration.joystick.exponentialDescription1') }}</p>
5650
<p class="text-xs text-gray-400 mb-2">
57-
Exponential scaling allows reducing the sensitivity of an axis near the center, for more precise
58-
control, which then steepens the curve near the edges to use the full range. A value of 1 will result
59-
in a linear curve, while greater values result in more rounded exponentials.
51+
{{ $t('configuration.joystick.exponentialDescription2') }}
6052
</p>
6153
</div>
6254
</div>
@@ -65,18 +57,18 @@
6557
<div class="flex items-center gap-6">
6658
<div class="flex items-center gap-2">
6759
<v-checkbox v-model="currentCalibration.deadband.enabled" density="compact" hide-details />
68-
<span>Deadband/Deadzone</span>
60+
<span>{{ $t('configuration.joystick.deadbandDeadzone') }}</span>
6961
</div>
7062
<div class="flex items-center gap-2">
7163
<v-checkbox v-model="currentCalibration.exponential.enabled" density="compact" hide-details />
72-
<span>Exponential Scaling</span>
64+
<span>{{ $t('configuration.joystick.exponentialScaling') }}</span>
7365
</div>
7466
</div>
7567
<!-- Deadband Calibration Section -->
7668
<div class="w-full">
7769
<div class="flex items-center justify-between mb-2">
7870
<span v-if="isCalibrating && calibratingAxis === null" class="text-xs text-blue-400 ml-2">
79-
Calibrating all axes...
71+
{{ $t('configuration.joystick.calibratingAllAxes') }}
8072
</span>
8173
<v-progress-linear
8274
v-if="isCalibrating && calibratingAxis === null"
@@ -95,7 +87,7 @@
9587
:key="index"
9688
class="border border-gray-700/60 rounded-lg py-2 px-4 bg-gray-900/60 flex flex-col"
9789
>
98-
<div class="flex w-full justify-center text-lg font-bold text-white mb-3">Axis {{ index }}</div>
90+
<div class="flex w-full justify-center text-lg font-bold text-white mb-3">{{ $t('configuration.joystick.axisLabel', { index }) }}</div>
9991
<div class="w-full h-40 relative">
10092
<!-- Axis labels -->
10193
<span class="font-mono text-xs text-gray-400 absolute top-[81px] right-[10px]">
@@ -237,11 +229,11 @@
237229
</div>
238230
</template>
239231
<template #actions>
240-
<v-btn variant="text" @click="cancelCalibration">Cancel</v-btn>
232+
<v-btn variant="text" @click="cancelCalibration">{{ $t('configuration.joystick.cancel') }}</v-btn>
241233
<div class="w-full" />
242-
<v-btn variant="text" :disabled="isCalibrating" @click="startCalibration()"> Auto calibrate deadzones </v-btn>
234+
<v-btn variant="text" :disabled="isCalibrating" @click="startCalibration()">{{ $t('configuration.joystick.autoCalibrateDeadzones') }}</v-btn>
243235

244-
<v-btn variant="text" :disabled="!allowSavingCalibration" @click="saveCalibration">Save</v-btn>
236+
<v-btn variant="text" :disabled="!allowSavingCalibration" @click="saveCalibration">{{ $t('configuration.joystick.save') }}</v-btn>
245237
</template>
246238
</InteractionDialog>
247239
</teleport>

src/components/mini-widgets/SatelliteIndicator.vue

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
<span class="font-mono font-semibold leading-4 text-end w-fit"
66
>{{ store.statusGPS.visibleSatellites }} {{ $t('indicators.sats') }}</span
77
>
8-
<span class="font-mono text-sm font-semibold leading-4 text-end w-fit">{{ store.statusGPS.fixType }}</span>
8+
<span class="font-mono text-sm font-semibold leading-4 text-end w-fit">
9+
{{ tFixType }}
10+
</span>
911
</div>
1012
</div>
1113
</template>
@@ -16,5 +18,32 @@ import { useMainVehicleStore } from '@/stores/mainVehicle'
1618
1719
datalogger.registerUsage(DatalogVariable.gpsFixType)
1820
datalogger.registerUsage(DatalogVariable.gpsVisibleSatellites)
21+
import { computed } from 'vue'
22+
import { useI18n } from 'vue-i18n'
23+
1924
const store = useMainVehicleStore()
25+
const { t } = useI18n()
26+
27+
const tFixType = computed(() => {
28+
const map: Record<string, string> = {
29+
'No GPS': 'NO_GPS',
30+
'No fix': 'NO_FIX',
31+
'2D fix': 'FIX_2D',
32+
'3D fix': 'FIX_3D',
33+
'DGPS fix': 'DGPS',
34+
'RTK float': 'RTK_FLOAT',
35+
'RTK fix': 'RTK_FIXED',
36+
'Static': 'STATIC',
37+
'PPP fix': 'PPP'
38+
}
39+
40+
const v = store.statusGPS.fixType
41+
const key = map[v]
42+
if (key) {
43+
return t(`indicators.fixTypes.${key}`)
44+
}
45+
46+
// Fallback to raw value
47+
return v
48+
})
2049
</script>

src/libs/joystick/protocols.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
import { type ProtocolAction, JoystickProtocolActionsMapping } from '@/types/joystick'
22

3-
import { availableCockpitActions } from './protocols/cockpit-actions'
3+
import { getAvailableCockpitActions } from './protocols/cockpit-actions'
44
import { availableDataLakeActions } from './protocols/data-lake'
55
import {
66
availableMavlinkManualControlButtonFunctions,
77
mavlinkManualControlAxes,
88
migrateMavlinkManualControlButtons,
99
} from './protocols/mavlink-manual-control'
10-
import { modifierKeyActions, otherAvailableActions } from './protocols/other'
10+
import { modifierKeyActions, otherAvailableActions, getOtherAvailableActions, getModifierKeyActions } from './protocols/other'
1111

1212
export const allAvailableAxes = (): ProtocolAction[] => {
1313
return [
1414
...Object.values(mavlinkManualControlAxes),
1515
...Object.values(availableDataLakeActions()),
16-
otherAvailableActions.no_function,
16+
getOtherAvailableActions().no_function,
1717
]
1818
}
1919

2020
export const allAvailableButtons = (): ProtocolAction[] => {
2121
return [
22-
...Object.values(availableCockpitActions),
22+
...Object.values(getAvailableCockpitActions()),
2323
...Object.values(availableMavlinkManualControlButtonFunctions),
24-
...Object.values(otherAvailableActions),
25-
...Object.values(modifierKeyActions),
24+
...Object.values(getOtherAvailableActions()),
25+
...Object.values(getModifierKeyActions()),
2626
...Object.values(availableDataLakeActions()),
2727
]
2828
}

src/libs/joystick/protocols/cockpit-actions.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/* eslint-disable prettier/prettier */
33
/* eslint-disable max-len */
44
import { type ProtocolAction,JoystickProtocol } from '@/types/joystick'
5+
import i18n from '@/plugins/i18n'
56

67
/**
78
* Possible functions in the MAVLink `MANUAL_CONTROL` message protocol
@@ -36,21 +37,24 @@ export class CockpitAction implements ProtocolAction {
3637
}
3738
}
3839

39-
// Predefined actions
40-
export const predefinedCockpitActions: { [key in CockpitActionsFunction]: CockpitAction } = {
41-
[CockpitActionsFunction.go_to_next_view]: new CockpitAction(CockpitActionsFunction.go_to_next_view, 'Go to next view'),
42-
[CockpitActionsFunction.go_to_previous_view]: new CockpitAction(CockpitActionsFunction.go_to_previous_view, 'Go to previous view'),
43-
[CockpitActionsFunction.toggle_full_screen]: new CockpitAction(CockpitActionsFunction.toggle_full_screen, 'Toggle full screen'),
44-
[CockpitActionsFunction.mavlink_arm]: new CockpitAction(CockpitActionsFunction.mavlink_arm, 'Mavlink arm'),
45-
[CockpitActionsFunction.mavlink_disarm]: new CockpitAction(CockpitActionsFunction.mavlink_disarm, 'Mavlink disarm'),
46-
[CockpitActionsFunction.toggle_bottom_bar]: new CockpitAction(CockpitActionsFunction.toggle_bottom_bar, 'Toggle bottom bar'),
47-
[CockpitActionsFunction.toggle_top_bar]: new CockpitAction(CockpitActionsFunction.toggle_top_bar, 'Toggle top bar'),
48-
[CockpitActionsFunction.start_recording_all_streams]: new CockpitAction(CockpitActionsFunction.start_recording_all_streams, 'Start recording all streams'),
49-
[CockpitActionsFunction.stop_recording_all_streams]: new CockpitAction(CockpitActionsFunction.stop_recording_all_streams, 'Stop recording all streams'),
50-
[CockpitActionsFunction.toggle_recording_all_streams]: new CockpitAction(CockpitActionsFunction.toggle_recording_all_streams, 'Toggle recording all streams'),
51-
[CockpitActionsFunction.take_snapshot]: new CockpitAction(CockpitActionsFunction.take_snapshot, 'Take snapshot'),
52-
[CockpitActionsFunction.hold_to_confirm]: new CockpitAction(CockpitActionsFunction.hold_to_confirm, 'Hold to confirm'),
53-
}
40+
// Getter function for dynamic translations
41+
export const getCockpitActions = (): { [key in CockpitActionsFunction]: CockpitAction } => ({
42+
[CockpitActionsFunction.go_to_next_view]: new CockpitAction(CockpitActionsFunction.go_to_next_view, i18n.global.t('configuration.joystick.goToNextView')),
43+
[CockpitActionsFunction.go_to_previous_view]: new CockpitAction(CockpitActionsFunction.go_to_previous_view, i18n.global.t('configuration.joystick.goToPreviousView')),
44+
[CockpitActionsFunction.toggle_full_screen]: new CockpitAction(CockpitActionsFunction.toggle_full_screen, i18n.global.t('configuration.joystick.toggleFullScreen')),
45+
[CockpitActionsFunction.mavlink_arm]: new CockpitAction(CockpitActionsFunction.mavlink_arm, i18n.global.t('configuration.joystick.mavlinkArm')),
46+
[CockpitActionsFunction.mavlink_disarm]: new CockpitAction(CockpitActionsFunction.mavlink_disarm, i18n.global.t('configuration.joystick.mavlinkDisarm')),
47+
[CockpitActionsFunction.toggle_bottom_bar]: new CockpitAction(CockpitActionsFunction.toggle_bottom_bar, i18n.global.t('configuration.joystick.toggleBottomBar')),
48+
[CockpitActionsFunction.toggle_top_bar]: new CockpitAction(CockpitActionsFunction.toggle_top_bar, i18n.global.t('configuration.joystick.toggleTopBar')),
49+
[CockpitActionsFunction.start_recording_all_streams]: new CockpitAction(CockpitActionsFunction.start_recording_all_streams, i18n.global.t('configuration.joystick.startRecordingAllStreams')),
50+
[CockpitActionsFunction.stop_recording_all_streams]: new CockpitAction(CockpitActionsFunction.stop_recording_all_streams, i18n.global.t('configuration.joystick.stopRecordingAllStreams')),
51+
[CockpitActionsFunction.toggle_recording_all_streams]: new CockpitAction(CockpitActionsFunction.toggle_recording_all_streams, i18n.global.t('configuration.joystick.toggleRecordingAllStreams')),
52+
[CockpitActionsFunction.take_snapshot]: new CockpitAction(CockpitActionsFunction.take_snapshot, i18n.global.t('configuration.joystick.takeSnapshot')),
53+
[CockpitActionsFunction.hold_to_confirm]: new CockpitAction(CockpitActionsFunction.hold_to_confirm, i18n.global.t('configuration.joystick.holdToConfirm')),
54+
})
55+
56+
// Predefined actions (for backward compatibility, initialized once)
57+
export const predefinedCockpitActions: { [key in CockpitActionsFunction]: CockpitAction } = getCockpitActions()
5458

5559
export type CockpitActionCallback = () => void
5660

@@ -72,15 +76,21 @@ interface CallbackEntry {
7276
* Responsible for routing cockpit actions
7377
*/
7478
export class CockpitActionsManager {
75-
availableActions: { [key in CockpitActionsFunction]: CockpitAction } = { ...predefinedCockpitActions }
7679
actionsCallbacks: Record<string, CallbackEntry> = {}
7780

81+
// Make availableActions a getter so it always returns fresh translations
82+
get availableActions(): { [key in CockpitActionsFunction]: CockpitAction } {
83+
return getCockpitActions()
84+
}
85+
7886
registerNewAction = (action: CockpitAction): void => {
79-
this.availableActions[action.id] = action
87+
// Note: This won't work with getter, but it's not commonly used
88+
console.warn('registerNewAction is not supported with dynamic translations')
8089
}
8190

8291
unregisterAction = (id: CockpitActionsFunction): void => {
83-
delete this.availableActions[id]
92+
// Note: This won't work with getter, but it's not commonly used
93+
console.warn('unregisterAction is not supported with dynamic translations')
8494
}
8595

8696
registerActionCallback = (action: CockpitAction, callback: CockpitActionCallback): string => {
@@ -132,8 +142,14 @@ export const executeActionCallback = (id: string): void => {
132142
cockpitActionsManager.executeActionCallback(id)
133143
}
134144

145+
// Export for backward compatibility
135146
export const availableCockpitActions = cockpitActionsManager.availableActions
136147

148+
// Export getter function for dynamic translations
149+
export const getAvailableCockpitActions = (): { [key in CockpitActionsFunction]: CockpitAction } => {
150+
return getCockpitActions()
151+
}
152+
137153
/**
138154
* Action configuration interface
139155
*/
Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { type ProtocolAction, CockpitModifierKeyOption, JoystickProtocol } from '@/types/joystick'
2+
import i18n from '@/plugins/i18n'
23

34
/**
45
* Possible other protocol functions
@@ -7,23 +8,29 @@ export enum OtherProtocol {
78
no_function = 'no_function',
89
}
910

10-
export const otherAvailableActions: { [key in OtherProtocol]: ProtocolAction } = {
11+
export const getOtherAvailableActions = (): { [key in OtherProtocol]: ProtocolAction } => ({
1112
[OtherProtocol.no_function]: {
1213
protocol: JoystickProtocol.Other,
1314
id: OtherProtocol.no_function,
14-
name: 'No function',
15+
name: i18n.global.t('configuration.joystick.noFunction'),
1516
},
16-
}
17+
})
18+
19+
// For backward compatibility
20+
export const otherAvailableActions = getOtherAvailableActions()
1721

18-
export const modifierKeyActions: { [key in CockpitModifierKeyOption]: ProtocolAction } = {
22+
export const getModifierKeyActions = (): { [key in CockpitModifierKeyOption]: ProtocolAction } => ({
1923
[CockpitModifierKeyOption.regular]: {
2024
protocol: JoystickProtocol.CockpitModifierKey,
2125
id: CockpitModifierKeyOption.regular,
22-
name: 'Regular',
26+
name: i18n.global.t('configuration.joystick.regular'),
2327
},
2428
[CockpitModifierKeyOption.shift]: {
2529
protocol: JoystickProtocol.CockpitModifierKey,
2630
id: CockpitModifierKeyOption.shift,
27-
name: 'Shift',
31+
name: i18n.global.t('configuration.joystick.shift'),
2832
},
29-
}
33+
})
34+
35+
// For backward compatibility
36+
export const modifierKeyActions = getModifierKeyActions()

src/libs/joystick/protocols/predefined-resources.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,28 @@ import {
99
import { MavCmd, MAVLinkType } from '@/libs/connection/m2r/messages/mavlink2rest-enum'
1010
import { getUnindentedString } from '@/libs/utils'
1111
import { customActionTypes } from '@/types/cockpit-actions'
12+
import i18n from '@/plugins/i18n'
1213

1314
export let mavlinkCameraZoomActionId: string | undefined = undefined
1415
export let mavlinkCameraFocusActionId: string | undefined = undefined
1516

1617
export const setupMavlinkCameraResources = (): void => {
1718
const commonVariableConfig = { type: 'number' as DataLakeVariableType, allowUserToChangeValue: true }
1819
// Initialize camera zoom variables
19-
createDataLakeVariable({ id: 'camera-zoom-decrease', name: 'Camera Zoom Decrease', ...commonVariableConfig }, 0)
20-
createDataLakeVariable({ id: 'camera-zoom-increase', name: 'Camera Zoom Increase', ...commonVariableConfig }, 0)
20+
createDataLakeVariable({ id: 'camera-zoom-decrease', name: i18n.global.t('configuration.joystick.cameraZoomDecrease'), ...commonVariableConfig }, 0)
21+
createDataLakeVariable({ id: 'camera-zoom-increase', name: i18n.global.t('configuration.joystick.cameraZoomIncrease'), ...commonVariableConfig }, 0)
2122

2223
// Initialize camera focus variables
23-
createDataLakeVariable({ id: 'camera-focus-decrease', name: 'Camera Focus Decrease', ...commonVariableConfig }, 0)
24-
createDataLakeVariable({ id: 'camera-focus-increase', name: 'Camera Focus Increase', ...commonVariableConfig }, 0)
24+
createDataLakeVariable({ id: 'camera-focus-decrease', name: i18n.global.t('configuration.joystick.cameraFocusDecrease'), ...commonVariableConfig }, 0)
25+
createDataLakeVariable({ id: 'camera-focus-increase', name: i18n.global.t('configuration.joystick.cameraFocusIncrease'), ...commonVariableConfig }, 0)
2526

2627
// Initialize camera zoom transforming function
2728
try {
2829
const func = getAllTransformingFunctions().find((f) => f.id === 'camera-zoom')
2930
if (!func) {
3031
createTransformingFunction(
3132
'camera-zoom',
32-
'Camera Zoom',
33+
i18n.global.t('configuration.joystick.cameraZoom'),
3334
'number',
3435
getUnindentedString(`
3536
const zoom = {{camera-zoom-increase}} - {{camera-zoom-decrease}}
@@ -48,7 +49,7 @@ export const setupMavlinkCameraResources = (): void => {
4849
if (!func) {
4950
createTransformingFunction(
5051
'camera-focus',
51-
'Camera Focus',
52+
i18n.global.t('configuration.joystick.cameraFocus'),
5253
'number',
5354
getUnindentedString(`
5455
const focus = {{camera-focus-increase}} - {{camera-focus-decrease}}

0 commit comments

Comments
 (0)