const storageKey = 'autonomy-studio.lms.language'; let language = 'ko'; try { language = localStorage.getItem(storageKey) === 'en' ? 'en' : 'ko'; } catch {} const english = { '벽 배치': 'Place walls', '맵 저장': 'Save map', '로봇 경로 표시': 'Show robot path', '센서 표시': 'Show sensors', '카메라 영상 표시': 'Show camera preview', '초기 위치 지정': 'Set initial pose', '목표 위치 지정': 'Set goal', '카메라 시점': 'Camera view', '미니맵 접기/펼치기': 'Toggle minimap', '미니맵 접기': 'Collapse minimap', '미니맵 펼치기': 'Expand minimap', '카메라 화면 접기/펼치기': 'Toggle camera preview', '카메라 화면 접기': 'Collapse camera preview', '카메라 화면 펼치기': 'Expand camera preview', '실시간 카메라 화면': 'Live camera preview', 'i 전진 · , 후진 · j/l 회전 · k 정지': 'i Forward · , Reverse · j/l Turn · k Stop', 'i 전진 · , 후진 · j/l 주행 중 조향 · k 정지': 'i Forward · , Reverse · j/l Steer while moving · k Stop', 'ROS 상태 접기/펼치기': 'Toggle ROS status', 'ROS 상태 접기': 'Collapse ROS status', 'ROS 상태 펼치기': 'Expand ROS status', 'ROS 상태': 'ROS status', '응답을 기다리는 중입니다.': 'Waiting for a response.', '맵을 저장하고 있습니다.': 'Saving the map.', '가제보 로봇 위치를 적용하지 못했습니다.': 'Failed to set the Gazebo robot pose.', '주행 시뮬레이션에서는 초기 위치를 한 번만 설정할 수 있습니다.': 'The initial pose can only be set once during navigation simulation.', '로봇 초기 위치 설정': 'Set initial robot pose', '3D 포인트클라우드를 표시 중입니다.': 'Displaying the 3D point cloud.', '모듈을 연결해주세요.': 'Connect your modules.', '맵 저장에 실패했습니다.': 'Failed to save the map.', '기존 목표를 취소하지 못했습니다.': 'Failed to cancel the previous goal.', '가제보 로봇 위치 적용 시간이 초과되었습니다.': 'Setting the Gazebo robot pose timed out.' }; export function t(key) { return language === 'en' ? (english[key] ?? key) : key; } // Only explicitly marked viewer UI is translated. Topic names and telemetry stay intact. export function setText(element, key) { element.dataset.i18n = key; element.textContent = t(key); } function refresh() { document.documentElement.lang = language; document.querySelectorAll('[data-i18n]').forEach(element => { element.textContent = t(element.dataset.i18n); }); for (const attribute of ['title', 'aria-label', 'alt']) { document.querySelectorAll(`[data-i18n-${attribute}]`).forEach(element => { element.setAttribute(attribute, t(element.getAttribute(`data-i18n-${attribute}`))); }); } } window.addEventListener('storage', event => { if (event.key !== storageKey) return; language = event.newValue === 'en' ? 'en' : 'ko'; refresh(); }); refresh(); // Embedded viewers may not share storage with the app (for example file URLs). window.addEventListener('message', event => { if (window.parent === window || event.source !== window.parent) return; if (event.data?.type !== 'lms:language' || !['ko', 'en'].includes(event.data.language)) return; language = event.data.language; refresh(); }); if (window.parent !== window) window.parent.postMessage({ type: 'lms:language:ready' }, '*');