361361 const [ todos , setTodos ] = useState ( initialHashState ?. todos ?? loadTodos ( ) ) ;
362362 const [ value , setValue ] = useState ( "" ) ;
363363 const [ filter , setFilter ] = useState ( initialHashState ?. filter ?? "all" ) ;
364- const [ flash , setFlash ] = useState ( initialHashState ? "已从 hash 恢复状态 " : "" ) ;
364+ const [ flash , setFlash ] = useState ( initialHashState ? "State restored from hash " : "" ) ;
365365
366366 useEffect ( ( ) => {
367367 localStorage . setItem ( STORAGE_KEY , JSON . stringify ( todos ) ) ;
382382 if ( ! next ) return ;
383383 setTodos ( next . todos ) ;
384384 setFilter ( next . filter ) ;
385- setFlash ( "已按 hash 更新 " ) ;
385+ setFlash ( "Updated from hash " ) ;
386386 } ;
387387 window . addEventListener ( "hashchange" , onHashChange ) ;
388388 return ( ) => window . removeEventListener ( "hashchange" , onHashChange ) ;
426426
427427 const resetDemo = ( ) => {
428428 setTodos ( [
429- { id : uid ( ) , text : "整理今天最重要的 3 件事 " , done : false } ,
429+ { id : uid ( ) , text : "List the top 3 priorities for today " , done : false } ,
430430 { id : uid ( ) , text : "Review PR #142" , done : true } ,
431- { id : uid ( ) , text : "给团队同步 demo 进展 " , done : false } ,
431+ { id : uid ( ) , text : "Share demo progress with the team " , done : false } ,
432432 ] ) ;
433433 setFilter ( "all" ) ;
434- setFlash ( "已重置示例 " ) ;
434+ setFlash ( "Demo reset " ) ;
435435 } ;
436436
437437 const copyShareLink = async ( ) => {
438438 try {
439439 await navigator . clipboard . writeText ( window . location . href ) ;
440- setFlash ( "分享链接已复制 " ) ;
440+ setFlash ( "Share link copied " ) ;
441441 } catch {
442- setFlash ( "复制失败,请手动复制地址栏 " ) ;
442+ setFlash ( "Copy failed. Please copy the address bar manually. " ) ;
443443 }
444444 } ;
445445
449449 < header className ="card-header ">
450450 < h1 className ="card-title "> React + shadcn Todo (Hash Share)</ h1 >
451451 < p className ="card-description ">
452- 状态自动同步到 URL hash,发链接即可复现当前页面。
452+ State syncs automatically to the URL hash, so sharing the link reproduces this view.
453453 </ p >
454454 </ header >
455455
456456 < div className ="card-content ">
457457 < div className ="row ">
458458 < input
459459 className ="input "
460- placeholder ="输入待办,例如:设计 runtime streaming API "
460+ placeholder ="Add a task, e.g. design runtime streaming API "
461461 value =${ value }
462462 onChange =${ ( event ) => setValue ( event . target . value ) }
463463 onKeyDown=${ onInputKeyDown }
464464 />
465465 < button className ="btn btn-primary " onClick =${ addTodo } disabled =${ ! value . trim ( ) } >
466- 添加
466+ Add
467467 </ button >
468468 </ div >
469469
470470 ${ filtered . length === 0
471- ? html `< div className ="todo-empty "> 当前筛选下没有任务。 </ div > `
471+ ? html `< div className ="todo-empty "> No tasks under the current filter. </ div > `
472472 : html `
473473 < ul className ="todo-list ">
474474 ${ filtered . map (
@@ -486,10 +486,10 @@ <h1 className="card-title">React + shadcn Todo (Hash Share)</h1>
486486 < button
487487 className ="btn btn-ghost "
488488 onClick =${ ( ) => removeTodo ( todo . id ) }
489- aria-label ="删除 "
490- title="删除 "
489+ aria-label ="Delete "
490+ title="Delete "
491491 >
492- 删除
492+ Delete
493493 </ button >
494494 </ li >
495495 ` ,
@@ -503,37 +503,37 @@ <h1 className="card-title">React + shadcn Todo (Hash Share)</h1>
503503 className =${ `btn btn-outline ${ filter === "all" ? "active" : "" } ` }
504504 onClick =${ ( ) => setFilter ( "all" ) }
505505 >
506- 全部
506+ All
507507 </ button >
508508 < button
509509 className =${ `btn btn-outline ${ filter === "active" ? "active" : "" } ` }
510510 onClick =${ ( ) => setFilter ( "active" ) }
511511 >
512- 进行中
512+ Active
513513 </ button >
514514 < button
515515 className =${ `btn btn-outline ${ filter === "done" ? "active" : "" } ` }
516516 onClick =${ ( ) => setFilter ( "done" ) }
517517 >
518- 已完成
518+ Completed
519519 </ button >
520520 </ div >
521521
522522 < div className ="row ">
523- < span className ="badge "> 剩余 ${ leftCount } 项 </ span >
524- < button className ="btn btn-danger " onClick =${ clearDone } > 清理已完成 </ button >
525- < button className ="btn btn-ghost " onClick =${ resetDemo } > 重置示例 </ button >
526- < button className ="btn btn-outline " onClick =${ copyShareLink } > 复制分享链接 </ button >
523+ < span className ="badge "> ${ leftCount } remaining </ span >
524+ < button className ="btn btn-danger " onClick =${ clearDone } > Clear completed </ button >
525+ < button className ="btn btn-ghost " onClick =${ resetDemo } > Reset demo </ button >
526+ < button className ="btn btn-outline " onClick =${ copyShareLink } > Copy share link </ button >
527527 </ div >
528528 </ div >
529529
530530 < div className ="hash-hint ">
531- Hash 字段: < code > #state64=<base64url(json)></ code >
531+ Hash field: < code > #state64=<base64url(json)></ code >
532532 ${ flash ? html `< div style =${ { marginTop : 6 , color : "#0f766e" , fontWeight : 600 } } > ${ flash } </ div > ` : null }
533533 </ div >
534534
535535 < p className ="muted " style =${ { marginTop : 12 , fontSize : 12 } } >
536- 提示:本地也会保存到 LocalStorage。手动改 hash 后会自动重载状态。
536+ Tip: state is also saved to LocalStorage. Manually editing the hash auto-reloads state.
537537 </ p >
538538 </ div >
539539 </ section >
0 commit comments