refactor(i18n): 移除 chrome.i18n 国际化,统一使用中文硬编码

移除 chromeI18n 工具、_locales 翻译文件和 manifest default_locale 配置,
将所有 UI 文案改为直接硬编码中文,并更新相关测试与文档。
This commit is contained in:
雨霖铃
2026-06-18 14:22:14 +00:00
parent 618f3c36f2
commit ec92afa294
68 changed files with 531 additions and 2148 deletions
+4 -11
View File
@@ -4,7 +4,6 @@ import { toast } from 'sonner';
import { CopyButton } from '@/components/CopyButton';
import type { UnitType } from '../constants';
import { msToUnit } from '../constants';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
interface LiveClockProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -13,8 +12,6 @@ interface LiveClockProps extends React.HTMLAttributes<HTMLDivElement> {
}
export default function LiveClock({ unit, onUseNow, className, ...props }: LiveClockProps) {
const { t } = useI18n('timestamp');
const [rawTime, setRawTime] = useState(() => Date.now());
useEffect(() => {
@@ -36,7 +33,7 @@ export default function LiveClock({ unit, onUseNow, className, ...props }: LiveC
{...props}
>
<span className="text-muted-foreground font-bold text-[10px] uppercase tracking-wider whitespace-nowrap shrink-0 selection:bg-transparent select-none">
{t('timestamp:currentTs')}
{'当前时间戳'}
</span>
<span className="flex-1 font-mono font-bold text-foreground text-sm tracking-tight leading-none truncate tabular-nums">
@@ -47,19 +44,15 @@ export default function LiveClock({ unit, onUseNow, className, ...props }: LiveC
type="button"
onClick={() => {
onUseNow(rawTime);
toast.success(t('timestamp:usedSuccess'));
toast.success('已使用当前时间戳');
}}
title={t('timestamp:useNowTooltip')}
title={'填充到下方'}
className="flex h-7 w-7 items-center justify-center rounded-md border border-input bg-background text-muted-foreground shadow-sm hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
>
<Clock className="w-3.5 h-3.5" />
</button>
<CopyButton
text={text}
tooltip={t('timestamp:copyTsTooltip')}
className="h-7 w-7 rounded-md border"
/>
<CopyButton text={text} tooltip={'复制时间戳'} className="h-7 w-7 rounded-md border" />
</div>
);
}
@@ -1,6 +1,5 @@
import React from 'react';
import { CopyButton } from '@/components/CopyButton';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
interface ResultViewProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -14,8 +13,6 @@ export default function ResultView({
className,
...props
}: ResultViewProps) {
const { t } = useI18n('timestamp');
if (!result) {
if (!showEmptyPlaceholder) return null;
return (
@@ -26,7 +23,7 @@ export default function ResultView({
)}
{...props}
>
{t('timestamp:resultEmpty')}
{'请输入并点击转换'}
</div>
);
}
@@ -34,7 +31,7 @@ export default function ResultView({
return (
<div className={cn('flex flex-col w-full', className)} {...props}>
<span className="block text-muted-foreground/90 mb-2.5 text-xs font-semibold tracking-wider uppercase">
{t('timestamp:resultLabel')}
{'转换结果'}
</span>
<div className="bg-card text-card-foreground border border-border p-4 sm:p-5 rounded-xl relative shadow-sm flex justify-between items-center gap-4 focus-within:ring-1 focus-within:ring-ring">
@@ -43,7 +40,7 @@ export default function ResultView({
</span>
<CopyButton
text={result}
tooltip={t('timestamp:copyResultTooltip')}
tooltip={'复制结果'}
className="h-8 w-8 rounded-md shrink-0 border"
/>
</div>
+7 -12
View File
@@ -4,7 +4,6 @@ import type { ModeType, UnitType, ZoneType } from './constants';
import LiveClock from './components/LiveClock';
import ResultView from './components/ResultView';
import { useTimestampConverter } from './useTimestampConverter';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
import { Input } from '@/components/ui/input';
@@ -17,18 +16,16 @@ import {
} from '@/components/ui/select';
const MODE_OPTIONS: { value: ModeType; label: string }[] = [
{ value: 'ts2dt', label: 'timestamp:tsToDate' },
{ value: 'dt2ts', label: 'timestamp:dateToTs' },
{ value: 'ts2dt', label: '时间戳转日期' },
{ value: 'dt2ts', label: '日期转时间戳' },
];
const UNIT_OPTIONS: { value: UnitType; label: string }[] = [
{ value: 'ms', label: 'timestamp:unitMs' },
{ value: 's', label: 'timestamp:unitS' },
{ value: 'ms', label: '毫秒' },
{ value: 's', label: '' },
];
export default function Index() {
const { t } = useI18n('timestamp');
const {
mode,
input,
@@ -53,7 +50,7 @@ export default function Index() {
<div className="flex flex-col gap-4">
<SwitchButtonGroup
value={mode}
options={MODE_OPTIONS.map((o) => ({ ...o, label: t(o.label) }))}
options={MODE_OPTIONS}
onChange={setMode}
size="small"
/>
@@ -61,9 +58,7 @@ export default function Index() {
<div className="flex flex-col gap-1.5">
<Input
type="text"
placeholder={
mode === 'ts2dt' ? t('timestamp:placeholderTs') : t('timestamp:placeholderDate')
}
placeholder={mode === 'ts2dt' ? '输入时间戳...' : 'YYYY-MM-DD HH:mm:ss'}
value={input}
onChange={(e) => setInput(e.target.value)}
className={cn(
@@ -78,7 +73,7 @@ export default function Index() {
<div className="flex flex-col sm:flex-row items-stretch gap-3 w-full">
<SwitchButtonGroup
value={unit}
options={UNIT_OPTIONS.map((o) => ({ ...o, label: t(o.label) }))}
options={UNIT_OPTIONS}
onChange={setUnit}
size="small"
className="sm:w-auto shrink-0"
+4 -6
View File
@@ -2,7 +2,6 @@ import { useMemo, useState } from 'react';
import dayjs from '@/utils/dayjs';
import type { UnitType, ZoneType, ModeType } from './constants';
import { DATE_FORMAT, msToUnit, dayjsFromTimestamp } from './constants';
import { useI18n } from '@/utils/chromeI18n';
import { useContextMenuData } from '@/utils/useContextMenuData';
export interface UseTimestampConverterReturn {
@@ -29,7 +28,6 @@ function isTimestampLike(input: string): boolean {
}
export function useTimestampConverter(): UseTimestampConverterReturn {
const { t } = useI18n('timestamp');
const [mode, setMode] = useState<ModeType>('ts2dt');
const [unit, setUnit] = useState<UnitType>('ms');
const [zone, setZone] = useState<ZoneType>('Asia/Shanghai');
@@ -43,22 +41,22 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
if (mode === 'ts2dt') {
const num = Number(rawInput);
if (isNaN(num)) {
return { result: '', error: t('timestamp:errors.invalidNumber') };
return { result: '', error: '请输入有效数字' };
}
const d = dayjsFromTimestamp(num, unit);
if (!d.isValid()) {
return { result: '', error: t('timestamp:errors.invalidTimestamp') };
return { result: '', error: '无效时间戳' };
}
return { result: d.tz(zone).format(DATE_FORMAT), error: '' };
} else {
const d = dayjs.tz(rawInput, DATE_FORMAT, zone);
if (!d.isValid()) {
return { result: '', error: t('timestamp:errors.invalidFormat') };
return { result: '', error: '无效的日期格式' };
}
const ms = d.valueOf();
return { result: String(msToUnit(ms, unit)), error: '' };
}
}, [input, mode, unit, zone, t]);
}, [input, mode, unit, zone]);
const handleContextMenuData = (payload: string) => {
const trimmed = payload.trim();