feat: implement comprehensive i18n internationalization support

- Add custom i18n infrastructure with TypeScript support
- Support Chinese (zh-CN) and English (en-US) languages
- Implement language switching with localStorage persistence
- Update all components with translation keys:
  * System info components (board-info-panel, board-index)
  * Display management components (display-state-index, display-state-card)
  * LED strip configuration components (led-strip-configuration, led-count-control-panel)
  * White balance component with detailed usage instructions
  * LED test component with test pattern descriptions
- Add comprehensive translation coverage for:
  * Navigation menus and page titles
  * Common UI elements (buttons, status, actions)
  * Hardware information and connection status
  * Display configuration options
  * LED strip settings and controls
  * White balance adjustment instructions and tips
  * LED test modes and descriptions
  * Error messages and status indicators
- Features:
  * Dynamic language switching without app restart
  * Type-safe translation keys with full TypeScript support
  * Modular design for easy language extension
  * Responsive UI updates using SolidJS reactivity
This commit is contained in:
2025-07-08 16:55:12 +08:00
parent 4a3d7681d6
commit 2c6b777fa6
16 changed files with 893 additions and 115 deletions

View File

@ -8,10 +8,12 @@ import { setLedStripStore } from './stores/led-strip.store';
import { LedStripConfigContainer } from './models/led-strip-config';
import { InfoIndex } from './components/info/info-index';
import { DisplayStateIndex } from './components/displays/display-state-index';
import { useLanguage } from './i18n/index';
function App() {
const location = useLocation();
const [previousPath, setPreviousPath] = createSignal<string>('');
const { t, locale, setLocale } = useLanguage();
// Monitor route changes and cleanup LED tests when leaving the test page
createEffect(() => {
@ -57,26 +59,49 @@ function App() {
</svg>
</div>
<ul class="menu menu-sm dropdown-content mt-3 z-[100] p-2 shadow bg-base-100 rounded-box w-52 border border-base-300">
<li><A href="/info" class="text-base-content hover:bg-base-200"></A></li>
<li><A href="/displays" class="text-base-content hover:bg-base-200"></A></li>
<li><A href="/led-strips-configuration" class="text-base-content hover:bg-base-200"></A></li>
<li><A href="/white-balance" class="text-base-content hover:bg-base-200"></A></li>
<li><A href="/led-strip-test" class="text-base-content hover:bg-base-200"></A></li>
<li><A href="/info" class="text-base-content hover:bg-base-200">{t('nav.info')}</A></li>
<li><A href="/displays" class="text-base-content hover:bg-base-200">{t('nav.displays')}</A></li>
<li><A href="/led-strips-configuration" class="text-base-content hover:bg-base-200">{t('nav.ledConfiguration')}</A></li>
<li><A href="/white-balance" class="text-base-content hover:bg-base-200">{t('nav.whiteBalance')}</A></li>
<li><A href="/led-strip-test" class="text-base-content hover:bg-base-200">{t('nav.ledTest')}</A></li>
</ul>
</div>
<a class="btn btn-ghost text-xl text-primary font-bold"></a>
<a class="btn btn-ghost text-xl text-primary font-bold">{t('nav.title')}</a>
</div>
<div class="navbar-center hidden lg:flex">
<ul class="menu menu-horizontal px-1">
<li><A href="/info" class="btn btn-ghost text-base-content hover:text-primary"></A></li>
<li><A href="/displays" class="btn btn-ghost text-base-content hover:text-primary"></A></li>
<li><A href="/led-strips-configuration" class="btn btn-ghost text-base-content hover:text-primary"></A></li>
<li><A href="/white-balance" class="btn btn-ghost text-base-content hover:text-primary"></A></li>
<li><A href="/led-strip-test" class="btn btn-ghost text-base-content hover:text-primary"></A></li>
<li><A href="/info" class="btn btn-ghost text-base-content hover:text-primary">{t('nav.info')}</A></li>
<li><A href="/displays" class="btn btn-ghost text-base-content hover:text-primary">{t('nav.displays')}</A></li>
<li><A href="/led-strips-configuration" class="btn btn-ghost text-base-content hover:text-primary">{t('nav.ledConfiguration')}</A></li>
<li><A href="/white-balance" class="btn btn-ghost text-base-content hover:text-primary">{t('nav.whiteBalance')}</A></li>
<li><A href="/led-strip-test" class="btn btn-ghost text-base-content hover:text-primary">{t('nav.ledTest')}</A></li>
</ul>
</div>
<div class="navbar-end">
<div class="badge badge-primary badge-outline">v1.0</div>
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm">
{locale() === 'zh-CN' ? '中文' : 'English'}
</div>
<ul class="dropdown-content z-[100] menu p-2 shadow bg-base-100 rounded-box w-32 border border-base-300">
<li>
<button
class={`${locale() === 'zh-CN' ? 'active' : ''}`}
onClick={() => setLocale('zh-CN')}
>
</button>
</li>
<li>
<button
class={`${locale() === 'en-US' ? 'active' : ''}`}
onClick={() => setLocale('en-US')}
>
English
</button>
</li>
</ul>
</div>
<div class="badge badge-primary badge-outline ml-2">v1.0</div>
</div>
</div>

View File

@ -1,5 +1,6 @@
import { Component, ParentComponent } from 'solid-js';
import { DisplayState } from '../../models/display-state.model';
import { useLanguage } from '../../i18n/index';
type DisplayStateCardProps = {
state: DisplayState;
@ -19,48 +20,49 @@ const Item: ParentComponent<ItemProps> = (props) => {
};
export const DisplayStateCard: Component<DisplayStateCardProps> = (props) => {
const { t } = useLanguage();
return (
<div class="card bg-base-200 shadow-lg hover:shadow-xl transition-shadow duration-200">
<div class="card-body p-4">
<div class="card-title text-base mb-3 flex items-center justify-between">
<span></span>
<div class="badge badge-primary badge-outline"></div>
<span>{t('displays.title')}</span>
<div class="badge badge-primary badge-outline">{t('common.realtime')}</div>
</div>
<div class="grid grid-cols-1 gap-3">
{/* 亮度信息 */}
<div class="bg-base-100 rounded-lg p-3">
<h4 class="text-sm font-semibold text-base-content mb-2"></h4>
<h4 class="text-sm font-semibold text-base-content mb-2">{t('displays.brightnessSettings')}</h4>
<div class="space-y-1">
<Item label="当前亮度">{props.state.brightness}</Item>
<Item label="最大亮度">{props.state.max_brightness}</Item>
<Item label="最小亮度">{props.state.min_brightness}</Item>
<Item label={t('displays.currentBrightness')}>{props.state.brightness}</Item>
<Item label={t('displays.maxBrightness')}>{props.state.max_brightness}</Item>
<Item label={t('displays.minBrightness')}>{props.state.min_brightness}</Item>
</div>
</div>
{/* 对比度信息 */}
<div class="bg-base-100 rounded-lg p-3">
<h4 class="text-sm font-semibold text-base-content mb-2"></h4>
<h4 class="text-sm font-semibold text-base-content mb-2">{t('displays.contrastSettings')}</h4>
<div class="space-y-1">
<Item label="当前对比度">{props.state.contrast}</Item>
<Item label="最大对比度">{props.state.max_contrast}</Item>
<Item label="最小对比度">{props.state.min_contrast}</Item>
<Item label={t('displays.currentContrast')}>{props.state.contrast}</Item>
<Item label={t('displays.maxContrast')}>{props.state.max_contrast}</Item>
<Item label={t('displays.minContrast')}>{props.state.min_contrast}</Item>
</div>
</div>
{/* 模式信息 */}
<div class="bg-base-100 rounded-lg p-3">
<h4 class="text-sm font-semibold text-base-content mb-2"></h4>
<h4 class="text-sm font-semibold text-base-content mb-2">{t('displays.modeSettings')}</h4>
<div class="space-y-1">
<Item label="当前模式">{props.state.mode}</Item>
<Item label="最大模式">{props.state.max_mode}</Item>
<Item label="最小模式">{props.state.min_mode}</Item>
<Item label={t('displays.currentMode')}>{props.state.mode}</Item>
<Item label={t('displays.maxMode')}>{props.state.max_mode}</Item>
<Item label={t('displays.minMode')}>{props.state.min_mode}</Item>
</div>
</div>
{/* 更新时间 */}
<div class="text-xs text-base-content/50 text-center pt-2 border-t border-base-300">
: {props.state.last_modified_at.toLocaleString()}
{t('displays.lastModified')}: {props.state.last_modified_at.toLocaleString()}
</div>
</div>
</div>

View File

@ -4,11 +4,13 @@ import debug from 'debug';
import { invoke } from '@tauri-apps/api/core';
import { DisplayState, RawDisplayState } from '../../models/display-state.model';
import { DisplayStateCard } from './display-state-card';
import { useLanguage } from '../../i18n/index';
const logger = debug('app:components:displays:display-state-index');
export const DisplayStateIndex: Component = () => {
const [states, setStates] = createSignal<DisplayState[]>([]);
const { t } = useLanguage();
createEffect(() => {
const unlisten = listen<RawDisplayState[]>('displays_changed', (ev) => {
@ -38,10 +40,10 @@ export const DisplayStateIndex: Component = () => {
return (
<div class="space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-base-content"></h1>
<h1 class="text-2xl font-bold text-base-content">{t('displays.title')}</h1>
<div class="stats shadow">
<div class="stat">
<div class="stat-title"></div>
<div class="stat-title">{t('displays.displayCount')}</div>
<div class="stat-value text-primary">{states().length}</div>
</div>
</div>
@ -63,8 +65,8 @@ export const DisplayStateIndex: Component = () => {
{states().length === 0 && (
<div class="text-center py-12">
<div class="text-6xl mb-4">🖥</div>
<h3 class="text-lg font-semibold text-base-content mb-2"></h3>
<p class="text-base-content/70"></p>
<h3 class="text-lg font-semibold text-base-content mb-2">{t('displays.noDisplaysFound')}</h3>
<p class="text-base-content/70">{t('displays.checkConnection')}</p>
</div>
)}
</div>

View File

@ -4,11 +4,13 @@ import { listen } from '@tauri-apps/api/event';
import debug from 'debug';
import { invoke } from '@tauri-apps/api/core';
import { BoardInfoPanel } from './board-info-panel';
import { useLanguage } from '../../i18n/index';
const logger = debug('app:components:info:board-index');
export const BoardIndex: Component = () => {
const [boards, setBoards] = createSignal<BoardInfo[]>([]);
const { t } = useLanguage();
createEffect(() => {
const unlisten = listen<BoardInfo[]>('boards_changed', (ev) => {
@ -28,10 +30,10 @@ export const BoardIndex: Component = () => {
return (
<div class="space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-base-content"></h1>
<h1 class="text-2xl font-bold text-base-content">{t('info.boardInfo')}</h1>
<div class="stats shadow">
<div class="stat">
<div class="stat-title"></div>
<div class="stat-title">{t('info.deviceCount')}</div>
<div class="stat-value text-primary">{boards().length}</div>
</div>
</div>
@ -53,8 +55,8 @@ export const BoardIndex: Component = () => {
{boards().length === 0 && (
<div class="text-center py-12">
<div class="text-6xl mb-4">🔍</div>
<h3 class="text-lg font-semibold text-base-content mb-2"></h3>
<p class="text-base-content/70"></p>
<h3 class="text-lg font-semibold text-base-content mb-2">{t('info.noDevicesFound')}</h3>
<p class="text-base-content/70">{t('info.checkConnection')}</p>
</div>
)}
</div>

View File

@ -1,5 +1,6 @@
import { Component, ParentComponent, createMemo } from 'solid-js';
import { BoardInfo } from '../../models/board-info.model';
import { useLanguage } from '../../i18n/index';
type ItemProps = {
label: string;
@ -15,6 +16,7 @@ const Item: ParentComponent<ItemProps> = (props) => {
};
export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => {
const { t } = useLanguage();
const ttl = createMemo(() => {
if (props.board.connect_status !== 'Connected') {
return '--';
@ -60,10 +62,10 @@ export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => {
<div class={statusBadgeClass()}>{connectStatus()}</div>
</div>
<div class="space-y-2">
<Item label="主机名">{props.board.host}</Item>
<Item label="IP地址">{props.board.address}</Item>
<Item label="端口">{props.board.port}</Item>
<Item label="延迟">{ttl()}</Item>
<Item label={t('info.hostname')}>{props.board.host}</Item>
<Item label={t('info.ipAddress')}>{props.board.address}</Item>
<Item label={t('info.port')}>{props.board.port}</Item>
<Item label={t('info.latency')}>{ttl()}</Item>
</div>
</div>
</div>

View File

@ -5,6 +5,7 @@ import { ledStripStore } from '../../stores/led-strip.store';
import { Borders } from '../../constants/border';
import { LedType } from '../../models/led-strip-config';
import { LedStripConfigurationContext } from '../../contexts/led-strip-configuration.context';
import { useLanguage } from '../../i18n/index';
type LedCountControlItemProps = {
displayId: number;
@ -14,6 +15,7 @@ type LedCountControlItemProps = {
const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
const [stripConfiguration, { setHoveredStripPart }] = useContext(LedStripConfigurationContext);
const { t } = useLanguage();
const config = createMemo(() => {
return ledStripStore.strips.find(
@ -116,7 +118,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
class="btn btn-xs btn-circle btn-outline flex-shrink-0 min-h-0 h-6 w-6"
onClick={handleDecrease}
disabled={!config() || (config()?.len || 0) <= 0}
title="减少LED数量"
title={t('ledConfig.decreaseLedCount')}
>
-
</button>
@ -139,7 +141,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
class="btn btn-xs btn-circle btn-outline flex-shrink-0 min-h-0 h-6 w-6"
onClick={handleIncrease}
disabled={!config() || (config()?.len || 0) >= 1000}
title="增加LED数量"
title={t('ledConfig.increaseLedCount')}
>
+
</button>
@ -150,7 +152,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
class="select select-xs w-full text-xs h-6 min-h-0"
value={config()?.led_type || LedType.RGB}
onChange={handleLedTypeChange}
title="LED类型"
title={t('ledConfig.ledType')}
>
<option value={LedType.RGB}>RGB</option>
<option value={LedType.RGBW}>RGBW</option>
@ -167,20 +169,21 @@ type LedCountControlPanelProps = {
export const LedCountControlPanel: Component<LedCountControlPanelProps> = (props) => {
const [localProps, rootProps] = splitProps(props, ['display']);
const { t } = useLanguage();
const borders: { border: Borders; label: string }[] = [
{ border: 'Top', label: '上' },
{ border: 'Bottom', label: '下' },
{ border: 'Left', label: '左' },
{ border: 'Right', label: '右' },
{ border: 'Top', label: t('ledConfig.top') },
{ border: 'Bottom', label: t('ledConfig.bottom') },
{ border: 'Left', label: t('ledConfig.left') },
{ border: 'Right', label: t('ledConfig.right') },
];
return (
<div {...rootProps} class={'card bg-base-200 shadow-lg border border-base-300 ' + (rootProps.class || '')}>
<div class="card-body p-3">
<div class="card-title text-sm mb-2 flex items-center justify-between">
<span>LED数量控制</span>
<div class="badge badge-info badge-outline text-xs"> {localProps.display.id}</div>
<span>{t('ledConfig.ledCountControl')}</span>
<div class="badge badge-info badge-outline text-xs">{t('ledConfig.display')} {localProps.display.id}</div>
</div>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-2">
@ -196,7 +199,7 @@ export const LedCountControlPanel: Component<LedCountControlPanelProps> = (props
</div>
<div class="text-xs text-base-content/50 mt-2 p-1.5 bg-base-300/50 rounded">
💡 +/- LED0-1000
💡 {t('ledConfig.controlTip')}
</div>
</div>
</div>

View File

@ -13,9 +13,11 @@ import {
LedStripConfigurationContext,
LedStripConfigurationContextType,
} from '../../contexts/led-strip-configuration.context';
import { useLanguage } from '../../i18n/index';
export const LedStripConfiguration = () => {
const { t } = useLanguage();
createEffect(() => {
invoke<string>('list_display_info').then((displays) => {
const parsedDisplays = JSON.parse(displays);
@ -106,10 +108,10 @@ export const LedStripConfiguration = () => {
return (
<div class="space-y-4">
<div class="flex items-center justify-between">
<h1 class="text-xl font-bold text-base-content"></h1>
<h1 class="text-xl font-bold text-base-content">{t('ledConfig.title')}</h1>
<div class="stats shadow">
<div class="stat py-2 px-4">
<div class="stat-title text-xs"></div>
<div class="stat-title text-xs">{t('displays.displayCount')}</div>
<div class="stat-value text-primary text-lg">{displayStore.displays.length}</div>
</div>
</div>
@ -121,12 +123,12 @@ export const LedStripConfiguration = () => {
<div class="card bg-base-200 shadow-lg">
<div class="card-body p-3">
<div class="card-title text-sm mb-2">
<span></span>
<div class="badge badge-info badge-outline text-xs"></div>
<span>{t('ledConfig.stripSorting')}</span>
<div class="badge badge-info badge-outline text-xs">{t('ledConfig.realtimePreview')}</div>
</div>
<LedStripPartsSorter />
<div class="text-xs text-base-content/50 mt-2">
💡
💡 {t('ledConfig.sortingTip')}
</div>
</div>
</div>
@ -135,8 +137,8 @@ export const LedStripConfiguration = () => {
<div class="card bg-base-200 shadow-lg">
<div class="card-body p-3">
<div class="card-title text-sm mb-2">
<span></span>
<div class="badge badge-secondary badge-outline text-xs"></div>
<span>{t('ledConfig.displayConfiguration')}</span>
<div class="badge badge-secondary badge-outline text-xs">{t('ledConfig.visualEditor')}</div>
</div>
<div class="mb-3">
<DisplayListContainer>
@ -146,7 +148,7 @@ export const LedStripConfiguration = () => {
</DisplayListContainer>
</div>
<div class="text-xs text-base-content/50">
💡 使LED数量
💡 {t('ledConfig.displayTip')}
</div>
</div>
</div>
@ -154,8 +156,8 @@ export const LedStripConfiguration = () => {
{/* LED Count Control Panels */}
<div class="flex-shrink-0">
<div class="flex items-center gap-2 mb-2">
<h2 class="text-base font-semibold text-base-content">LED数量控制</h2>
<div class="badge badge-info badge-outline text-xs"></div>
<h2 class="text-base font-semibold text-base-content">{t('ledConfig.ledCountControl')}</h2>
<div class="badge badge-info badge-outline text-xs">{t('ledConfig.realtimeAdjustment')}</div>
</div>
<div class="led-control-grid">
{displayStore.displays.map((display) => (

View File

@ -1,6 +1,7 @@
import { createSignal, createEffect, For, Show, onCleanup } from 'solid-js';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
import { useLanguage } from '../../i18n/index';
interface BoardInfo {
fullname: string;
@ -24,6 +25,7 @@ interface TestEffectConfig {
}
export const LedStripTest = () => {
const { t } = useLanguage();
const [boards, setBoards] = createSignal<BoardInfo[]>([]);
const [selectedBoard, setSelectedBoard] = createSignal<BoardInfo | null>(null);
const [ledCount, setLedCount] = createSignal(60);
@ -100,23 +102,23 @@ export const LedStripTest = () => {
// Test patterns
const testPatterns: TestPattern[] = [
{
name: '流光效果',
description: '彩虹色流光,用于测试灯带方向',
name: t('ledTest.flowingRainbow'),
description: t('ledTest.flowingRainbowDesc'),
effect_type: 'FlowingRainbow'
},
{
name: '十个一组计数',
description: '每十个LED一组不同颜色用于快速计算灯珠数量',
name: t('ledTest.groupCounting'),
description: t('ledTest.groupCountingDesc'),
effect_type: 'GroupCounting'
},
{
name: '单色扫描',
description: '单个LED依次点亮用于精确测试每个LED位置',
name: t('ledTest.singleScan'),
description: t('ledTest.singleScanDesc'),
effect_type: 'SingleScan'
},
{
name: '呼吸灯',
description: '整条灯带呼吸效果,用于测试整体亮度',
name: t('ledTest.breathing'),
description: t('ledTest.breathingDesc'),
effect_type: 'Breathing'
}
];

View File

@ -10,6 +10,7 @@ import { BiRegularReset } from 'solid-icons/bi';
import { BsFullscreen, BsFullscreenExit } from 'solid-icons/bs';
import { getCurrentWindow } from '@tauri-apps/api/window';
import transparentBg from '../../assets/transparent-grid-background.svg?url';
import { useLanguage } from '../../i18n/index';
const Value: Component<{ value: number }> = (props) => {
return (
@ -20,6 +21,7 @@ const Value: Component<{ value: number }> = (props) => {
};
export const WhiteBalance = () => {
const { t } = useLanguage();
const [isFullscreen, setIsFullscreen] = createSignal(false);
const [panelPosition, setPanelPosition] = createSignal({ x: 0, y: 0 });
const [isDragging, setIsDragging] = createSignal(false);
@ -170,19 +172,19 @@ export const WhiteBalance = () => {
{!isFullscreen() && (
<div class="space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-base-content"></h1>
<h1 class="text-2xl font-bold text-base-content">{t('whiteBalance.title')}</h1>
<div class="flex gap-2">
<button class="btn btn-outline btn-sm" onClick={toggleFullscreen} title="进入全屏">
<button class="btn btn-outline btn-sm" onClick={toggleFullscreen} title={t('common.fullscreen')}>
<BsFullscreen size={16} />
{t('common.fullscreen')}
</button>
<button class="btn btn-outline btn-sm" onClick={reset} title="重置到100%">
<button class="btn btn-outline btn-sm" onClick={reset} title={t('common.reset')}>
<BiRegularReset size={16} />
{t('common.reset')}
</button>
<button class="btn btn-primary btn-sm" onClick={exit} title="返回">
<button class="btn btn-primary btn-sm" onClick={exit} title={t('whiteBalance.back')}>
<VsClose size={16} />
{t('whiteBalance.back')}
</button>
</div>
</div>
@ -192,8 +194,8 @@ export const WhiteBalance = () => {
<div class="card bg-base-200 shadow-lg">
<div class="card-body p-4">
<div class="card-title text-base mb-3">
<span></span>
<div class="badge badge-info badge-outline"></div>
<span>{t('whiteBalance.colorTest')}</span>
<div class="badge badge-info badge-outline">{t('whiteBalance.clickToTest')}</div>
</div>
<div
class="aspect-square rounded-lg overflow-hidden border border-base-300"
@ -204,7 +206,7 @@ export const WhiteBalance = () => {
<TestColorsBg />
</div>
<div class="text-xs text-base-content/50 mt-2">
💡
💡 {t('whiteBalance.colorTestTip')}
</div>
</div>
</div>
@ -213,14 +215,14 @@ export const WhiteBalance = () => {
<div class="card bg-base-200 shadow-lg">
<div class="card-body p-4">
<div class="card-title text-base mb-3">
<span>RGB调节</span>
<div class="badge badge-secondary badge-outline"></div>
<span>{t('whiteBalance.rgbAdjustment')}</span>
<div class="badge badge-secondary badge-outline">{t('whiteBalance.realtimeAdjustment')}</div>
</div>
<div class="space-y-4">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-red-500"> (R)</span>
<span class="label-text font-semibold text-red-500">{t('whiteBalance.redChannel')}</span>
<Value value={ledStripStore.colorCalibration.r} />
</label>
<ColorSlider
@ -237,7 +239,7 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-green-500">绿 (G)</span>
<span class="label-text font-semibold text-green-500">{t('whiteBalance.greenChannel')}</span>
<Value value={ledStripStore.colorCalibration.g} />
</label>
<ColorSlider
@ -254,7 +256,7 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-blue-500"> (B)</span>
<span class="label-text font-semibold text-blue-500">{t('whiteBalance.blueChannel')}</span>
<Value value={ledStripStore.colorCalibration.b} />
</label>
<ColorSlider
@ -271,7 +273,7 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-amber-500"> (W)</span>
<span class="label-text font-semibold text-amber-500">{t('whiteBalance.whiteChannel')}</span>
<Value value={ledStripStore.colorCalibration.w} />
</label>
<ColorSlider
@ -291,37 +293,37 @@ export const WhiteBalance = () => {
<div class="collapse collapse-arrow bg-base-100 mt-4">
<input type="checkbox" />
<div class="collapse-title text-sm font-medium text-base-content/80">
💡 使
💡 {t('whiteBalance.usageInstructions')}
</div>
<div class="collapse-content text-xs text-base-content/70 space-y-3">
<div class="space-y-2">
<p class="font-semibold text-primary">🎯 使</p>
<p class="font-semibold text-primary">{t('whiteBalance.recommendedMethod')}</p>
<ol class="list-decimal list-inside space-y-1 ml-2">
<li>"全屏"</li>
<li></li>
<li>{t('whiteBalance.fullscreenTip')}</li>
<li>{t('whiteBalance.dragTip')}</li>
<li>RGB控制面板拖拽到合适位置</li>
<li>LED灯条颜色与屏幕边缘颜色</li>
</ol>
</div>
<div class="space-y-2">
<p class="font-semibold text-secondary">🔧 </p>
<p class="font-semibold text-secondary">{t('whiteBalance.adjustmentTips')}</p>
<ul class="list-disc list-inside space-y-1 ml-2">
<li><span class="text-red-500 font-medium"></span>R值LED会减少红色成分</li>
<li><span class="text-green-500 font-medium">绿</span>G值LED会减少绿色成分</li>
<li><span class="text-blue-500 font-medium"></span>B值LED会减少蓝色成分</li>
<li><span class="text-base-content font-medium"></span>B值R/G值</li>
<li><span class="text-base-content font-medium"></span>B值R/G值</li>
<li>{t('whiteBalance.redStrong')}</li>
<li>{t('whiteBalance.greenStrong')}</li>
<li>{t('whiteBalance.blueStrong')}</li>
<li>{t('whiteBalance.whiteYellow')}</li>
<li>{t('whiteBalance.whiteBlue')}</li>
</ul>
</div>
<div class="space-y-2">
<p class="font-semibold text-accent">📋 </p>
<p class="font-semibold text-accent">{t('whiteBalance.comparisonMethod')}</p>
<ul class="list-disc list-inside space-y-1 ml-2">
<li>LED白光与屏幕白色一致</li>
<li>LED颜色饱和度合适</li>
<li></li>
<li>"重置"</li>
<li>{t('whiteBalance.whiteComparison')}</li>
<li>{t('whiteBalance.colorComparison')}</li>
<li>{t('whiteBalance.environmentTest')}</li>
<li>{t('whiteBalance.resetNote')}</li>
</ul>
</div>
</div>
@ -356,10 +358,10 @@ export const WhiteBalance = () => {
>
<div class="flex items-center gap-2">
<span class="text-xs opacity-60"></span>
<span>RGB调节</span>
<div class="badge badge-secondary badge-outline"></div>
<span>{t('whiteBalance.rgbAdjustment')}</span>
<div class="badge badge-secondary badge-outline">{t('whiteBalance.draggable')}</div>
</div>
<button class="btn btn-ghost btn-xs cursor-pointer" onClick={toggleFullscreen} title="退出全屏">
<button class="btn btn-ghost btn-xs cursor-pointer" onClick={toggleFullscreen} title={t('whiteBalance.exitFullscreen')}>
<BsFullscreenExit size={14} />
</button>
</div>
@ -367,7 +369,7 @@ export const WhiteBalance = () => {
<div class="space-y-4">
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-red-500"> (R)</span>
<span class="label-text font-semibold text-red-500">{t('whiteBalance.redChannel')}</span>
<Value value={ledStripStore.colorCalibration.r} />
</label>
<ColorSlider
@ -384,7 +386,7 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-green-500">绿 (G)</span>
<span class="label-text font-semibold text-green-500">{t('whiteBalance.greenChannel')}</span>
<Value value={ledStripStore.colorCalibration.g} />
</label>
<ColorSlider
@ -401,7 +403,7 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-blue-500"> (B)</span>
<span class="label-text font-semibold text-blue-500">{t('whiteBalance.blueChannel')}</span>
<Value value={ledStripStore.colorCalibration.b} />
</label>
<ColorSlider
@ -418,7 +420,7 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-amber-500"> (W)</span>
<span class="label-text font-semibold text-amber-500">{t('whiteBalance.whiteChannel')}</span>
<Value value={ledStripStore.colorCalibration.w} />
</label>
<ColorSlider
@ -435,25 +437,25 @@ export const WhiteBalance = () => {
<div class="form-control">
<label class="label">
<span class="label-text font-semibold text-base-content/70"> (W)</span>
<div class="badge badge-outline badge-sm"></div>
<span class="label-text font-semibold text-base-content/70">{t('whiteBalance.whiteChannel')}</span>
<div class="badge badge-outline badge-sm">{t('whiteBalance.notEnabled')}</div>
</label>
<ColorSlider class="from-yellow-50 to-cyan-50" disabled />
</div>
</div>
<div class="text-xs text-base-content/60 mt-3 p-2 bg-base-300/50 rounded">
💡 LED灯条RGB滑块使颜色一致
💡 {t('whiteBalance.fullscreenComparisonTip')}
</div>
<div class="flex gap-2 mt-4">
<button class="btn btn-outline btn-sm flex-1" onClick={reset} title="重置到100%">
<button class="btn btn-outline btn-sm flex-1" onClick={reset} title={t('common.reset')}>
<BiRegularReset size={14} />
{t('common.reset')}
</button>
<button class="btn btn-primary btn-sm flex-1" onClick={exit} title="返回">
<button class="btn btn-primary btn-sm flex-1" onClick={exit} title={t('whiteBalance.back')}>
<VsClose size={14} />
{t('whiteBalance.back')}
</button>
</div>
</div>

79
src/i18n/index.tsx Normal file
View File

@ -0,0 +1,79 @@
import { createSignal, createContext, useContext, ParentComponent, createEffect } from 'solid-js';
import { Language, TranslationDict } from './types';
import { zhCN } from './locales/zh-CN';
import { enUS } from './locales/en-US';
// Available translations
const translations: Record<Language, TranslationDict> = {
'zh-CN': zhCN,
'en-US': enUS,
};
// Create locale signal
const [locale, setLocale] = createSignal<Language>('zh-CN');
// Translation function
const t = (key: string): string => {
const keys = key.split('.');
let value: any = translations[locale()];
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
return key; // Return key if translation not found
}
}
return typeof value === 'string' ? value : key;
};
// Language context for managing language state
interface LanguageContextType {
locale: () => Language;
setLocale: (lang: Language) => void;
t: (key: string) => string;
}
const LanguageContext = createContext<LanguageContextType>();
// Language provider component
export const LanguageProvider: ParentComponent = (props) => {
// Load saved language preference from localStorage
createEffect(() => {
const savedLang = localStorage.getItem('app-language') as Language;
if (savedLang && (savedLang === 'zh-CN' || savedLang === 'en-US')) {
setLocale(savedLang);
}
});
// Save language preference when it changes
createEffect(() => {
localStorage.setItem('app-language', locale());
});
const contextValue: LanguageContextType = {
locale,
setLocale,
t,
};
return (
<LanguageContext.Provider value={contextValue}>
{props.children}
</LanguageContext.Provider>
);
};
// Hook to use language context
export const useLanguage = () => {
const context = useContext(LanguageContext);
if (!context) {
throw new Error('useLanguage must be used within a LanguageProvider');
}
return context;
};
// Export types and utilities
export type { Language, TranslationDict };
export { translations };

211
src/i18n/locales/en-US.ts Normal file
View File

@ -0,0 +1,211 @@
import { TranslationDict } from '../types';
export const enUS: TranslationDict = {
nav: {
title: 'Ambient Light Control',
info: 'System Info',
displays: 'Display Info',
ledConfiguration: 'LED Configuration',
whiteBalance: 'White Balance',
ledTest: 'LED Test',
},
common: {
version: 'Version',
primary: 'Primary',
save: 'Save',
cancel: 'Cancel',
reset: 'Reset',
close: 'Close',
fullscreen: 'Fullscreen',
exitFullscreen: 'Exit Fullscreen',
loading: 'Loading...',
error: 'Error',
success: 'Success',
warning: 'Warning',
confirm: 'Confirm',
delete: 'Delete',
edit: 'Edit',
add: 'Add',
remove: 'Remove',
enable: 'Enable',
disable: 'Disable',
start: 'Start',
stop: 'Stop',
test: 'Test',
apply: 'Apply',
refresh: 'Refresh',
realtime: 'Real-time',
},
info: {
title: 'System Information',
boardInfo: 'Device Information',
systemInfo: 'System Information',
deviceName: 'Device Name',
ipAddress: 'IP Address',
macAddress: 'MAC Address',
firmwareVersion: 'Firmware Version',
hardwareVersion: 'Hardware Version',
uptime: 'Uptime',
status: 'Status',
connected: 'Connected',
disconnected: 'Disconnected',
lastSeen: 'Last Seen',
port: 'Port',
latency: 'Latency',
hostname: 'Hostname',
deviceCount: 'Device Count',
noDevicesFound: 'No Devices Found',
checkConnection: 'Please check device connection status',
},
displays: {
title: 'Display Status',
count: 'Display Count',
noDisplays: 'No displays detected',
checkConnection: 'Please check display connection',
displayInfo: 'Display Information',
resolution: 'Resolution',
refreshRate: 'Refresh Rate',
colorDepth: 'Color Depth',
isPrimary: 'Primary Display',
position: 'Position',
size: 'Size',
scaleFactor: 'Scale Factor',
lastModified: 'Last Modified',
displayCount: 'Display Count',
noDisplaysFound: 'No displays detected',
brightnessSettings: 'Brightness Settings',
currentBrightness: 'Current Brightness',
maxBrightness: 'Max Brightness',
minBrightness: 'Min Brightness',
contrastSettings: 'Contrast Settings',
currentContrast: 'Current Contrast',
maxContrast: 'Max Contrast',
minContrast: 'Min Contrast',
modeSettings: 'Mode Settings',
currentMode: 'Current Mode',
maxMode: 'Max Mode',
minMode: 'Min Mode',
},
ledConfig: {
title: 'LED Strip Configuration',
displaySelection: 'Display Selection',
ledStripConfig: 'LED Strip Configuration',
ledCount: 'LED Count',
ledType: 'LED Type',
position: 'Position',
top: 'Top',
bottom: 'Bottom',
left: 'Left',
right: 'Right',
preview: 'Preview',
configuration: 'Configuration',
sorter: 'Sorter',
moveUp: 'Move Up',
moveDown: 'Move Down',
reverse: 'Reverse',
rgb: 'RGB',
rgbw: 'RGBW',
segments: 'Segments',
totalLeds: 'Total LEDs',
saveConfig: 'Save Configuration',
loadConfig: 'Load Configuration',
stripSorting: 'LED Strip Sorting',
realtimePreview: 'Real-time Preview',
sortingTip: 'Tip: Drag LED strip segments to adjust order, double-click to reverse direction',
displayConfiguration: 'Display Configuration',
visualEditor: 'Visual Editor',
displayTip: 'Tip: Hover over displays for detailed information, use control panel below to adjust LED count',
ledCountControl: 'LED Count Control',
realtimeAdjustment: 'Real-time Adjustment',
decreaseLedCount: 'Decrease LED Count',
increaseLedCount: 'Increase LED Count',
display: 'Display',
controlTip: 'Tip: Click +/- buttons or input values directly to adjust LED count (Range: 0-1000)',
},
whiteBalance: {
title: 'White Balance Adjustment',
colorCalibration: 'Color Calibration',
redChannel: 'Red (R)',
greenChannel: 'Green (G)',
blueChannel: 'Blue (B)',
whiteChannel: 'White (W)',
brightness: 'Brightness',
temperature: 'Temperature',
resetToDefault: 'Reset to Default',
fullscreenMode: 'Fullscreen Mode',
normalMode: 'Normal Mode',
instructions: 'Instructions',
helpText: 'Adjust RGB values to match screen colors with actual LED strip colors',
compareColors: 'Compare Colors',
adjustValues: 'Adjust Values',
dragToMove: 'Drag to Move Panel',
back: 'Back',
colorTest: 'Color Test',
clickToTest: 'Click to Test',
colorTestTip: 'Tip: Click color blocks for single color test, click again to return to multi-color mode',
rgbAdjustment: 'RGB Adjustment',
realtimeAdjustment: 'Real-time Adjustment',
usageInstructions: 'White Balance Adjustment Instructions',
recommendedMethod: '🎯 Recommended Method:',
adjustmentTips: '🔧 Adjustment Tips:',
comparisonMethod: '📋 Comparison Method:',
fullscreenTip: 'Click "Fullscreen" button above to enter fullscreen mode',
dragTip: 'Color strips will be displayed at screen edges in fullscreen mode',
redStrong: 'Red too strong: Lower R value to reduce red component in LEDs',
greenStrong: 'Green too strong: Lower G value to reduce green component in LEDs',
blueStrong: 'Blue too strong: Lower B value to reduce blue component in LEDs',
whiteYellow: 'White appears yellow: Increase B value, decrease R/G values',
whiteBlue: 'White appears blue: Decrease B value, increase R/G values',
whiteComparison: 'Focus on white areas, ensure LED white light matches screen white',
colorComparison: 'Check color areas, ensure LED color saturation is appropriate',
environmentTest: 'Test under different ambient lighting to ensure stable results',
resetNote: 'Click "Reset" button to restore default values after adjustment',
fullscreenComparisonTip: 'Compare screen edge colors with LED strips, adjust RGB sliders to match colors',
draggable: 'Draggable',
exitFullscreen: 'Exit Fullscreen',
notEnabled: 'Not Enabled',
},
ledTest: {
title: 'LED Strip Test',
testEffects: 'Test Effects',
staticColor: 'Static Color',
rainbow: 'Rainbow',
breathing: 'Breathing',
wave: 'Wave',
chase: 'Chase',
twinkle: 'Twinkle',
fire: 'Fire',
speed: 'Speed',
brightness: 'Brightness',
color: 'Color',
startTest: 'Start Test',
stopTest: 'Stop Test',
testRunning: 'Test Running',
testStopped: 'Test Stopped',
selectEffect: 'Select Effect',
effectSettings: 'Effect Settings',
flowingRainbow: 'Flowing Rainbow',
flowingRainbowDesc: 'Rainbow flowing light for testing LED strip direction',
groupCounting: 'Group Counting',
groupCountingDesc: 'Different colors for every ten LEDs to quickly count LED quantity',
singleScan: 'Single Scan',
singleScanDesc: 'Light up each LED individually for precise position testing',
breathingDesc: 'Breathing effect for the entire LED strip to test overall brightness',
},
errors: {
failedToLoad: 'Failed to load',
failedToSave: 'Failed to save',
failedToConnect: 'Failed to connect',
invalidConfiguration: 'Invalid configuration',
deviceNotFound: 'Device not found',
networkError: 'Network error',
unknownError: 'Unknown error',
},
};

211
src/i18n/locales/zh-CN.ts Normal file
View File

@ -0,0 +1,211 @@
import { TranslationDict } from '../types';
export const zhCN: TranslationDict = {
nav: {
title: '环境光控制',
info: '基本信息',
displays: '显示器信息',
ledConfiguration: '灯条配置',
whiteBalance: '白平衡',
ledTest: '灯带测试',
},
common: {
version: '版本',
primary: '主要',
save: '保存',
cancel: '取消',
reset: '重置',
close: '关闭',
fullscreen: '全屏',
exitFullscreen: '退出全屏',
loading: '加载中...',
error: '错误',
success: '成功',
warning: '警告',
confirm: '确认',
delete: '删除',
edit: '编辑',
add: '添加',
remove: '移除',
enable: '启用',
disable: '禁用',
start: '开始',
stop: '停止',
test: '测试',
apply: '应用',
refresh: '刷新',
realtime: '实时',
},
info: {
title: '基本信息',
boardInfo: '设备信息',
systemInfo: '系统信息',
deviceName: '设备名称',
ipAddress: 'IP地址',
macAddress: 'MAC地址',
firmwareVersion: '固件版本',
hardwareVersion: '硬件版本',
uptime: '运行时间',
status: '状态',
connected: '已连接',
disconnected: '已断开',
lastSeen: '最后连接',
port: '端口',
latency: '延迟',
hostname: '主机名',
deviceCount: '设备总数',
noDevicesFound: '未发现设备',
checkConnection: '请检查设备连接状态',
},
displays: {
title: '显示器状态',
count: '显示器数量',
noDisplays: '未检测到显示器',
checkConnection: '请检查显示器连接状态',
displayInfo: '显示器信息',
resolution: '分辨率',
refreshRate: '刷新率',
colorDepth: '色深',
isPrimary: '主显示器',
position: '位置',
size: '尺寸',
scaleFactor: '缩放比例',
lastModified: '最后修改',
displayCount: '显示器数量',
noDisplaysFound: '未检测到显示器',
brightnessSettings: '亮度设置',
currentBrightness: '当前亮度',
maxBrightness: '最大亮度',
minBrightness: '最小亮度',
contrastSettings: '对比度设置',
currentContrast: '当前对比度',
maxContrast: '最大对比度',
minContrast: '最小对比度',
modeSettings: '模式设置',
currentMode: '当前模式',
maxMode: '最大模式',
minMode: '最小模式',
},
ledConfig: {
title: '灯条配置',
displaySelection: '显示器选择',
ledStripConfig: '灯条配置',
ledCount: '灯珠数量',
ledType: '灯珠类型',
position: '位置',
top: '顶部',
bottom: '底部',
left: '左侧',
right: '右侧',
preview: '预览',
configuration: '配置',
sorter: '排序器',
moveUp: '上移',
moveDown: '下移',
reverse: '反转',
rgb: 'RGB',
rgbw: 'RGBW',
segments: '段数',
totalLeds: '总灯珠数',
saveConfig: '保存配置',
loadConfig: '加载配置',
stripSorting: '灯条排序',
realtimePreview: '实时预览',
sortingTip: '提示:拖拽灯条段落来调整顺序,双击可反转方向',
displayConfiguration: '显示器配置',
visualEditor: '可视化编辑',
displayTip: '提示悬停显示器查看详细信息使用下方控制面板调整LED数量',
ledCountControl: 'LED数量控制',
realtimeAdjustment: '实时调整',
decreaseLedCount: '减少LED数量',
increaseLedCount: '增加LED数量',
display: '显示器',
controlTip: '提示:点击 +/- 按钮或直接输入数值来调整LED数量范围0-1000',
},
whiteBalance: {
title: '白平衡调节',
colorCalibration: '颜色校准',
redChannel: '红色 (R)',
greenChannel: '绿色 (G)',
blueChannel: '蓝色 (B)',
whiteChannel: '白色 (W)',
brightness: '亮度',
temperature: '色温',
resetToDefault: '重置为默认值',
fullscreenMode: '全屏模式',
normalMode: '普通模式',
instructions: '使用说明',
helpText: '调整RGB值以匹配屏幕颜色与LED灯条的实际颜色',
compareColors: '比较颜色',
adjustValues: '调整数值',
dragToMove: '拖拽移动面板',
back: '返回',
colorTest: '颜色测试',
clickToTest: '点击测试',
colorTestTip: '提示:点击颜色块进行单色测试,再次点击返回多色模式',
rgbAdjustment: 'RGB调节',
realtimeAdjustment: '实时调节',
usageInstructions: '白平衡调节使用说明',
recommendedMethod: '🎯 推荐使用方法:',
adjustmentTips: '🔧 调节技巧:',
comparisonMethod: '📋 对比方法:',
fullscreenTip: '点击上方"全屏"按钮进入全屏模式',
dragTip: '全屏模式下屏幕边缘会显示彩色条带',
redStrong: '红色偏强降低R值LED会减少红色成分',
greenStrong: '绿色偏强降低G值LED会减少绿色成分',
blueStrong: '蓝色偏强降低B值LED会减少蓝色成分',
whiteYellow: '白色发黄适当提高B值降低R/G值',
whiteBlue: '白色发蓝适当降低B值提高R/G值',
whiteComparison: '重点观察白色区域确保LED白光与屏幕白色一致',
colorComparison: '检查彩色区域确保LED颜色饱和度合适',
environmentTest: '在不同环境光下测试,确保效果稳定',
resetNote: '调节完成后可点击"重置"按钮恢复默认值',
fullscreenComparisonTip: '对比屏幕边缘颜色与LED灯条调节RGB滑块使颜色一致',
draggable: '可拖拽',
exitFullscreen: '退出全屏',
notEnabled: '暂未启用',
},
ledTest: {
title: '灯带测试',
testEffects: '测试效果',
staticColor: '静态颜色',
rainbow: '彩虹',
breathing: '呼吸',
wave: '波浪',
chase: '追逐',
twinkle: '闪烁',
fire: '火焰',
speed: '速度',
brightness: '亮度',
color: '颜色',
startTest: '开始测试',
stopTest: '停止测试',
testRunning: '测试运行中',
testStopped: '测试已停止',
selectEffect: '选择效果',
effectSettings: '效果设置',
flowingRainbow: '流光效果',
flowingRainbowDesc: '彩虹色流光,用于测试灯带方向',
groupCounting: '十个一组计数',
groupCountingDesc: '每十个LED一组不同颜色用于快速计算灯珠数量',
singleScan: '单色扫描',
singleScanDesc: '单个LED依次点亮用于精确测试每个LED位置',
breathingDesc: '整条灯带呼吸效果,用于测试整体亮度',
},
errors: {
failedToLoad: '加载失败',
failedToSave: '保存失败',
failedToConnect: '连接失败',
invalidConfiguration: '配置无效',
deviceNotFound: '设备未找到',
networkError: '网络错误',
unknownError: '未知错误',
},
};

219
src/i18n/types.ts Normal file
View File

@ -0,0 +1,219 @@
export type Language = 'zh-CN' | 'en-US';
export interface TranslationDict {
// Navigation
nav: {
title: string;
info: string;
displays: string;
ledConfiguration: string;
whiteBalance: string;
ledTest: string;
};
// Common UI elements
common: {
version: string;
primary: string;
save: string;
cancel: string;
reset: string;
close: string;
fullscreen: string;
exitFullscreen: string;
loading: string;
error: string;
success: string;
warning: string;
confirm: string;
delete: string;
edit: string;
add: string;
remove: string;
enable: string;
disable: string;
start: string;
stop: string;
test: string;
apply: string;
refresh: string;
realtime: string;
};
// Info page
info: {
title: string;
boardInfo: string;
systemInfo: string;
deviceName: string;
ipAddress: string;
macAddress: string;
firmwareVersion: string;
hardwareVersion: string;
uptime: string;
status: string;
connected: string;
disconnected: string;
lastSeen: string;
port: string;
latency: string;
hostname: string;
deviceCount: string;
noDevicesFound: string;
checkConnection: string;
};
// Display page
displays: {
title: string;
count: string;
noDisplays: string;
checkConnection: string;
displayInfo: string;
resolution: string;
refreshRate: string;
colorDepth: string;
isPrimary: string;
position: string;
size: string;
scaleFactor: string;
lastModified: string;
displayCount: string;
noDisplaysFound: string;
brightnessSettings: string;
currentBrightness: string;
maxBrightness: string;
minBrightness: string;
contrastSettings: string;
currentContrast: string;
maxContrast: string;
minContrast: string;
modeSettings: string;
currentMode: string;
maxMode: string;
minMode: string;
};
// LED Strip Configuration
ledConfig: {
title: string;
displaySelection: string;
ledStripConfig: string;
ledCount: string;
ledType: string;
position: string;
top: string;
bottom: string;
left: string;
right: string;
preview: string;
configuration: string;
sorter: string;
moveUp: string;
moveDown: string;
reverse: string;
rgb: string;
rgbw: string;
segments: string;
totalLeds: string;
saveConfig: string;
loadConfig: string;
stripSorting: string;
realtimePreview: string;
sortingTip: string;
displayConfiguration: string;
visualEditor: string;
displayTip: string;
ledCountControl: string;
realtimeAdjustment: string;
decreaseLedCount: string;
increaseLedCount: string;
display: string;
controlTip: string;
};
// White Balance
whiteBalance: {
title: string;
colorCalibration: string;
redChannel: string;
greenChannel: string;
blueChannel: string;
whiteChannel: string;
brightness: string;
temperature: string;
resetToDefault: string;
fullscreenMode: string;
normalMode: string;
instructions: string;
helpText: string;
compareColors: string;
adjustValues: string;
dragToMove: string;
back: string;
colorTest: string;
clickToTest: string;
colorTestTip: string;
rgbAdjustment: string;
realtimeAdjustment: string;
usageInstructions: string;
recommendedMethod: string;
adjustmentTips: string;
comparisonMethod: string;
fullscreenTip: string;
dragTip: string;
redStrong: string;
greenStrong: string;
blueStrong: string;
whiteYellow: string;
whiteBlue: string;
whiteComparison: string;
colorComparison: string;
environmentTest: string;
resetNote: string;
fullscreenComparisonTip: string;
draggable: string;
exitFullscreen: string;
notEnabled: string;
};
// LED Test
ledTest: {
title: string;
testEffects: string;
staticColor: string;
rainbow: string;
breathing: string;
wave: string;
chase: string;
twinkle: string;
fire: string;
speed: string;
brightness: string;
color: string;
startTest: string;
stopTest: string;
testRunning: string;
testStopped: string;
selectEffect: string;
effectSettings: string;
flowingRainbow: string;
flowingRainbowDesc: string;
groupCounting: string;
groupCountingDesc: string;
singleScan: string;
singleScanDesc: string;
breathingDesc: string;
};
// Error messages
errors: {
failedToLoad: string;
failedToSave: string;
failedToConnect: string;
invalidConfiguration: string;
deviceNotFound: string;
networkError: string;
unknownError: string;
};
}

View File

@ -4,12 +4,15 @@ import { render } from "solid-js/web";
import "./styles.css";
import App from "./App";
import { Router } from '@solidjs/router';
import { LanguageProvider } from './i18n/index';
render(
() => (
<Router>
<App />
</Router>
<LanguageProvider>
<Router>
<App />
</Router>
</LanguageProvider>
),
document.getElementById('root') as HTMLElement,
);