[FEAT] next-intl 기반 다국어(영어/한국어) 지원 세팅#105
Conversation
- next-intl 패키지를 의존성에 추가했습니다
- locale 라우팅, 미들웨어, 번역 리소스, root layout의 NextIntlClientProvider 배선을 추가했습니다 - 영어를 기본 locale로, as-needed 프리픽스 전략을 적용했습니다
- i18n 라우팅 적용을 위해 기존 라우트 폴더들을 locale 동적 세그먼트 하위로 이동했습니다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthroughnext-intl 기반 i18n 지원을 위해 routing/navigation/request 설정, locale 감지 미들웨어, next.config.js 플러그인 연동을 추가했습니다. 기존 루트 레이아웃을 삭제하고 locale-aware 레이아웃/홈 페이지를 신설했으며, 사이드바 라벨을 번역 키로 전환하고 home 관련 파일들의 import 경로를 Changesnext-intl i18n 세팅
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant Proxy as proxy.ts (middleware)
participant Layout as app/[locale]/layout.tsx
participant Request as i18n/request.ts
participant Messages as messages/*.json
Browser->>Proxy: /home 요청
Proxy->>Proxy: routing 기반 locale 결정
Proxy-->>Browser: /[locale]/home 리다이렉트
Browser->>Layout: /[locale]/home 요청
Layout->>Layout: hasLocale로 유효성 검사
Layout->>Request: requestLocale 전달
Request->>Messages: locale 메시지 동적 import
Messages-->>Request: messages
Request-->>Layout: locale, messages
Layout-->>Browser: NextIntlClientProvider로 감싼 페이지 렌더링
Possibly related PRs
Suggested labels: Suggested reviewers: 지역화 코드 리뷰 한 줄 촌평: 하드코딩된 "Hi Timo" 인사말을 걷어내고 다국어 인프라를 깔끔하게 놓으셨네요, 굿잡! 🐰🌏 다만 이슈에 명시된 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/timo-web/app/`[locale]/page.tsx:
- Around line 3-7: The Home component currently renders the translated title
inside a Fragment, so it lacks the required heading semantics. Update the Home
component to wrap the result of useTranslations("HomePage") in a single h1
element, keeping only one h1 on the page and ensuring the homepage title is
exposed as the primary heading for accessibility and SEO.
In `@apps/timo-web/package.json`:
- Line 20: The dependency version policy is inconsistent in package.json: next
is pinned exactly, but next-intl still uses a caret range. Update the next-intl
entry to match the repo’s pinning approach by using an exact version, and keep
this change aligned with the existing dependency declarations in the same
package manifest.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0c155641-886f-4d84-a4eb-6a28453d6523
⛔ Files ignored due to path filters (2)
apps/timo-web/app/[locale]/fonts/PretendardVariable.woff2is excluded by!**/*.woff2pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (47)
apps/timo-web/app/[locale]/focus/_components/.gitkeepapps/timo-web/app/[locale]/focus/_containers/.gitkeepapps/timo-web/app/[locale]/focus/_queries/.gitkeepapps/timo-web/app/[locale]/focus/page.tsxapps/timo-web/app/[locale]/globals.cssapps/timo-web/app/[locale]/home/_components/.gitkeepapps/timo-web/app/[locale]/home/_containers/.gitkeepapps/timo-web/app/[locale]/home/_queries/.gitkeepapps/timo-web/app/[locale]/home/page.tsxapps/timo-web/app/[locale]/layout.tsxapps/timo-web/app/[locale]/login/page.tsxapps/timo-web/app/[locale]/onboarding/_components/.gitkeepapps/timo-web/app/[locale]/onboarding/_containers/.gitkeepapps/timo-web/app/[locale]/onboarding/_queries/.gitkeepapps/timo-web/app/[locale]/onboarding/page.tsxapps/timo-web/app/[locale]/page.tsxapps/timo-web/app/[locale]/settings/_components/.gitkeepapps/timo-web/app/[locale]/settings/_containers/.gitkeepapps/timo-web/app/[locale]/settings/_queries/.gitkeepapps/timo-web/app/[locale]/settings/account/_components/.gitkeepapps/timo-web/app/[locale]/settings/account/_containers/.gitkeepapps/timo-web/app/[locale]/settings/account/_queries/.gitkeepapps/timo-web/app/[locale]/settings/account/page.tsxapps/timo-web/app/[locale]/settings/layout.tsxapps/timo-web/app/[locale]/settings/page.tsxapps/timo-web/app/[locale]/settings/policy/_components/.gitkeepapps/timo-web/app/[locale]/settings/policy/_containers/.gitkeepapps/timo-web/app/[locale]/settings/policy/_queries/.gitkeepapps/timo-web/app/[locale]/settings/policy/page.tsxapps/timo-web/app/[locale]/statistics/_components/.gitkeepapps/timo-web/app/[locale]/statistics/_containers/.gitkeepapps/timo-web/app/[locale]/statistics/_queries/.gitkeepapps/timo-web/app/[locale]/statistics/page.tsxapps/timo-web/app/[locale]/today/_components/.gitkeepapps/timo-web/app/[locale]/today/_containers/.gitkeepapps/timo-web/app/[locale]/today/_queries/.gitkeepapps/timo-web/app/[locale]/today/page.tsxapps/timo-web/app/layout.tsxapps/timo-web/app/page.tsxapps/timo-web/i18n/navigation.tsapps/timo-web/i18n/request.tsapps/timo-web/i18n/routing.tsapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.jsonapps/timo-web/next.config.jsapps/timo-web/package.jsonapps/timo-web/proxy.ts
💤 Files with no reviewable changes (2)
- apps/timo-web/app/page.tsx
- apps/timo-web/app/layout.tsx
- navigation.ts, request.ts, proxy.ts에서 상대 경로로 되어 있던 routing import를 @/i18n/routing 절대 경로로 통일했습니다
develop의 (main) 라우트 그룹 구조를 이 브랜치의 next-intl [locale] 세그먼트와
결합하여 app/[locale]/(main)/{focus,home,settings,statistics,today} 구조로
재정렬한다. NavigationSidebar는 로케일 인식 라우팅을 위해 next/link 대신
@/i18n/navigation의 Link/usePathname을 사용하도록 수정하고, 폰트 경로를
apps/timo-web/fonts로 옮겨진 실제 위치에 맞춰 수정한다.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/timo-web/proxy.ts (1)
7-9: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winmatcher에
_vercel제외 패턴 추가를 검토해주세요! 🕵️현재 matcher는
api,_next, 확장자 포함 경로만 제외하고 있는데, next-intl 공식 문서는 Vercel 배포 시/_vercel/...내부 rewrite 경로도 함께 제외하도록 권장합니다. 이 앱이 Vercel에 배포된다면 Analytics/Insights 같은 내부 엔드포인트가 의도치 않게 locale 미들웨어에 걸릴 수 있습니다.🔧 제안하는 수정
export const config = { - matcher: ["/((?!api|_next|.*\\..*).*)"], + matcher: ["/((?!api|trpc|_next|_vercel|.*\\..*).*)"], };관련 문서: next-intl Proxy/Middleware 가이드
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/proxy.ts` around lines 7 - 9, The proxy matcher in config should also exclude Vercel internal routes so locale middleware doesn’t intercept them. Update the matcher in proxy.ts to keep the existing api, _next, and static-extension exclusions while adding an explicit _vercel exclusion pattern, following the next-intl Proxy/Middleware guidance. Keep the change localized to the exported config.matcher definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx:
- Around line 84-94: TodoCreateModal mixes hardcoded English and Korean strings,
and its UI text is not wired to next-intl. Move all visible labels and
aria-labels in TodoCreateModal to useTranslations so they resolve from locale
messages consistently, including the button/title text and the modal controls
referenced by the current hardcoded strings. Make sure the component uses
translation keys for the strings currently shown in the modal so locale changes
affect every label uniformly.
- Around line 135-142: The TodoCreateModal textarea currently uses outline-none
without any replacement focus indicator, which violates the accessibility rule.
Update the textarea in TodoCreateModal to keep a visible keyboard focus state by
adding a proper focus-visible style such as a ring or border while removing the
raw outline suppression only if replaced. Make sure the change preserves clear
focus feedback for the textarea element and follows the app’s accessibility
convention for tsx components.
- Line 20: Rename the union type alias `TodoSubtaskVariant` to
`TodoSubtaskVariantTypes` to match the `Types` suffix convention for union
aliases. Update every reference that uses it, including
`TodoSubtaskItem.variant`, `SUBTASK_LABEL_STYLE`, and the `subtasks.map` usage
in `TodoCreateModal`, so the new type name is used consistently throughout the
component.
- Line 1: TodoCreateModal is a client-side, event-handling component and should
not live under the pure presentational _components_ layer. Move the logic in
TodoCreateModal to an appropriate _containers_ location and keep only
props-driven UI in _components_, preserving the existing client behavior by
keeping the component’s onClick/onChange handling in the container-level entry
point.
In `@apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx`:
- Around line 68-69: Rename the boolean helper in NavigationSidebar from
isActivePath to a convention-compliant name such as checkIsActivePath, and
update every call site in the same component to use the new identifier. Keep the
logic in the helper unchanged; this is only a naming consistency fix for the
pathname/href active-state check.
---
Outside diff comments:
In `@apps/timo-web/proxy.ts`:
- Around line 7-9: The proxy matcher in config should also exclude Vercel
internal routes so locale middleware doesn’t intercept them. Update the matcher
in proxy.ts to keep the existing api, _next, and static-extension exclusions
while adding an explicit _vercel exclusion pattern, following the next-intl
Proxy/Middleware guidance. Keep the change localized to the exported
config.matcher definition.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5d3433d4-a8d7-496c-a21c-c2dad56eff57
⛔ Files ignored due to path filters (1)
apps/timo-web/fonts/PretendardVariable.woff2is excluded by!**/*.woff2
📒 Files selected for processing (36)
apps/timo-web/app/[locale]/(main)/focus/_components/.gitkeepapps/timo-web/app/[locale]/(main)/focus/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/focus/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/focus/page.tsxapps/timo-web/app/[locale]/(main)/home/_components/.gitkeepapps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsxapps/timo-web/app/[locale]/(main)/home/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/home/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/home/page.tsxapps/timo-web/app/[locale]/(main)/layout.tsxapps/timo-web/app/[locale]/(main)/settings/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/page.tsxapps/timo-web/app/[locale]/(main)/settings/layout.tsxapps/timo-web/app/[locale]/(main)/settings/page.tsxapps/timo-web/app/[locale]/(main)/settings/policy/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/policy/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/policy/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/policy/page.tsxapps/timo-web/app/[locale]/(main)/statistics/_components/.gitkeepapps/timo-web/app/[locale]/(main)/statistics/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/statistics/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/statistics/page.tsxapps/timo-web/app/[locale]/(main)/today/_components/.gitkeepapps/timo-web/app/[locale]/(main)/today/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/today/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/today/page.tsxapps/timo-web/app/[locale]/layout.tsxapps/timo-web/components/layout/sidebar/NavigationSidebar.tsxapps/timo-web/i18n/navigation.tsapps/timo-web/i18n/request.tsapps/timo-web/proxy.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/timo-web/proxy.ts (1)
7-9: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winmatcher에
_vercel제외 패턴 추가를 검토해주세요! 🕵️현재 matcher는
api,_next, 확장자 포함 경로만 제외하고 있는데, next-intl 공식 문서는 Vercel 배포 시/_vercel/...내부 rewrite 경로도 함께 제외하도록 권장합니다. 이 앱이 Vercel에 배포된다면 Analytics/Insights 같은 내부 엔드포인트가 의도치 않게 locale 미들웨어에 걸릴 수 있습니다.🔧 제안하는 수정
export const config = { - matcher: ["/((?!api|_next|.*\\..*).*)"], + matcher: ["/((?!api|trpc|_next|_vercel|.*\\..*).*)"], };관련 문서: next-intl Proxy/Middleware 가이드
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/proxy.ts` around lines 7 - 9, The proxy matcher in config should also exclude Vercel internal routes so locale middleware doesn’t intercept them. Update the matcher in proxy.ts to keep the existing api, _next, and static-extension exclusions while adding an explicit _vercel exclusion pattern, following the next-intl Proxy/Middleware guidance. Keep the change localized to the exported config.matcher definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx:
- Around line 84-94: TodoCreateModal mixes hardcoded English and Korean strings,
and its UI text is not wired to next-intl. Move all visible labels and
aria-labels in TodoCreateModal to useTranslations so they resolve from locale
messages consistently, including the button/title text and the modal controls
referenced by the current hardcoded strings. Make sure the component uses
translation keys for the strings currently shown in the modal so locale changes
affect every label uniformly.
- Around line 135-142: The TodoCreateModal textarea currently uses outline-none
without any replacement focus indicator, which violates the accessibility rule.
Update the textarea in TodoCreateModal to keep a visible keyboard focus state by
adding a proper focus-visible style such as a ring or border while removing the
raw outline suppression only if replaced. Make sure the change preserves clear
focus feedback for the textarea element and follows the app’s accessibility
convention for tsx components.
- Line 20: Rename the union type alias `TodoSubtaskVariant` to
`TodoSubtaskVariantTypes` to match the `Types` suffix convention for union
aliases. Update every reference that uses it, including
`TodoSubtaskItem.variant`, `SUBTASK_LABEL_STYLE`, and the `subtasks.map` usage
in `TodoCreateModal`, so the new type name is used consistently throughout the
component.
- Line 1: TodoCreateModal is a client-side, event-handling component and should
not live under the pure presentational _components_ layer. Move the logic in
TodoCreateModal to an appropriate _containers_ location and keep only
props-driven UI in _components_, preserving the existing client behavior by
keeping the component’s onClick/onChange handling in the container-level entry
point.
In `@apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx`:
- Around line 68-69: Rename the boolean helper in NavigationSidebar from
isActivePath to a convention-compliant name such as checkIsActivePath, and
update every call site in the same component to use the new identifier. Keep the
logic in the helper unchanged; this is only a naming consistency fix for the
pathname/href active-state check.
---
Outside diff comments:
In `@apps/timo-web/proxy.ts`:
- Around line 7-9: The proxy matcher in config should also exclude Vercel
internal routes so locale middleware doesn’t intercept them. Update the matcher
in proxy.ts to keep the existing api, _next, and static-extension exclusions
while adding an explicit _vercel exclusion pattern, following the next-intl
Proxy/Middleware guidance. Keep the change localized to the exported
config.matcher definition.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5d3433d4-a8d7-496c-a21c-c2dad56eff57
⛔ Files ignored due to path filters (1)
apps/timo-web/fonts/PretendardVariable.woff2is excluded by!**/*.woff2
📒 Files selected for processing (36)
apps/timo-web/app/[locale]/(main)/focus/_components/.gitkeepapps/timo-web/app/[locale]/(main)/focus/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/focus/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/focus/page.tsxapps/timo-web/app/[locale]/(main)/home/_components/.gitkeepapps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsxapps/timo-web/app/[locale]/(main)/home/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/home/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/home/page.tsxapps/timo-web/app/[locale]/(main)/layout.tsxapps/timo-web/app/[locale]/(main)/settings/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/account/page.tsxapps/timo-web/app/[locale]/(main)/settings/layout.tsxapps/timo-web/app/[locale]/(main)/settings/page.tsxapps/timo-web/app/[locale]/(main)/settings/policy/_components/.gitkeepapps/timo-web/app/[locale]/(main)/settings/policy/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/settings/policy/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/settings/policy/page.tsxapps/timo-web/app/[locale]/(main)/statistics/_components/.gitkeepapps/timo-web/app/[locale]/(main)/statistics/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/statistics/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/statistics/page.tsxapps/timo-web/app/[locale]/(main)/today/_components/.gitkeepapps/timo-web/app/[locale]/(main)/today/_containers/.gitkeepapps/timo-web/app/[locale]/(main)/today/_queries/.gitkeepapps/timo-web/app/[locale]/(main)/today/page.tsxapps/timo-web/app/[locale]/layout.tsxapps/timo-web/components/layout/sidebar/NavigationSidebar.tsxapps/timo-web/i18n/navigation.tsapps/timo-web/i18n/request.tsapps/timo-web/proxy.ts
🛑 Comments failed to post (5)
apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx (4)
1-1: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # _components 디렉터리 내 다른 'use client' 컴포넌트 사례가 있는지 확인 rg -n "use client" apps/timo-web/app --glob '*_components*'Repository: Team-Timo/Timo-client
Length of output: 159
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== TodoCreateModal.tsx ==" sed -n '1,240p' apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx echo echo "== nearby files ==" find apps/timo-web/app/[locale]/(main)/home -maxdepth 3 -type f | sort echo echo "== component/containers structure under home ==" find apps/timo-web/app/[locale]/(main)/home -maxdepth 2 -type d | sortRepository: Team-Timo/Timo-client
Length of output: 238
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE='apps/timo-web/app/[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx' BASE='apps/timo-web/app/[locale]/(main)/home' echo "== TodoCreateModal.tsx ==" sed -n '1,240p' "$FILE" echo echo "== nearby files ==" find "$BASE" -maxdepth 3 -type f | sort echo echo "== component/containers structure under home ==" find "$BASE" -maxdepth 2 -type d | sortRepository: Team-Timo/Timo-client
Length of output: 6534
_components/보다는_containers/로 옮겨주세요
이 컴포넌트는onClick/onChange를 직접 처리하는 클라이언트 컴포넌트라,_components/의 “props만 받는 순수 UI” 규칙과는 조금 어긋납니다. 상태/이벤트 결합은_containers/로 분리하고,_components/에는 프레젠테이션만 남기는 쪽이 구조가 더 깔끔합니다.
참고: Next.js Client Components, 컴포넌트 구조 가이드🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx at line 1, TodoCreateModal is a client-side, event-handling component and should not live under the pure presentational _components_ layer. Move the logic in TodoCreateModal to an appropriate _containers_ location and keep only props-driven UI in _components_, preserving the existing client behavior by keeping the component’s onClick/onChange handling in the container-level entry point.Source: Path instructions
20-20: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
유니언 타입 별칭에
Types접미사가 빠졌습니다.
TodoSubtaskVariant는 유니언 타입 별칭인데, 컨벤션상Types접미사가 필요합니다 (TodoSubtaskVariantTypes). 이름 하나로 컨벤션 경찰을 피할 수 있어요 👮.✏️ 제안 diff
-export type TodoSubtaskVariant = "filled" | "placeholder"; +export type TodoSubtaskVariantTypes = "filled" | "placeholder";(그리고 이 타입을 참조하는
TodoSubtaskItem.variant,SUBTASK_LABEL_STYLE,subtasks.map내 사용처도 함께 변경 필요)As per path instructions, "
typealias는 유니언·튜플·리터럴에만 사용, 접미사Types".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx at line 20, Rename the union type alias `TodoSubtaskVariant` to `TodoSubtaskVariantTypes` to match the `Types` suffix convention for union aliases. Update every reference that uses it, including `TodoSubtaskItem.variant`, `SUBTASK_LABEL_STYLE`, and the `subtasks.map` usage in `TodoCreateModal`, so the new type name is used consistently throughout the component.Source: Path instructions
84-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift
영어/한국어 텍스트가 뒤섞여 있고, next-intl 연동이 빠져 있어요.
"Add a task", "Add an icon", "Please enter a note..", "Create" 같은 영어 UI 텍스트와
aria-label="닫기",aria-label="우선순위 설정",노트같은 한국어 라벨이 한 컴포넌트에 공존합니다. 이번 PR이 next-intl 기반 다국어 지원을 도입하는 목적이니, 이 모달의 문자열들도useTranslations로 옮기면 좋을 것 같습니다. 지금 상태로는 로케일이ko여도 화면엔 영어가 그대로 나올 텐데, 이건 국제화라기보단 "국제 짬뽕화"에 가깝네요 🍜.PR 설명에 따르면 메시지 마이그레이션은 후속 PR에서 진행 예정이라고 하니, 이 컴포넌트도 해당 후속 작업 범위에 포함되는지 확인 부탁드립니다.
Also applies to: 106-108, 132-140, 154-156, 166-167, 173-173, 183-183, 187-187, 193-193
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx around lines 84 - 94, TodoCreateModal mixes hardcoded English and Korean strings, and its UI text is not wired to next-intl. Move all visible labels and aria-labels in TodoCreateModal to useTranslations so they resolve from locale messages consistently, including the button/title text and the modal controls referenced by the current hardcoded strings. Make sure the component uses translation keys for the strings currently shown in the modal so locale changes affect every label uniformly.
135-142: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
포커스 아웃라인 제거는 접근성 가이드라인 위반입니다.
textarea에
outline-none을 적용하면서 대체 포커스 스타일이 없습니다. 키보드 사용자가 포커스 위치를 인지할 수 없게 되니,focus-visible:ring등으로 대체 스타일을 꼭 넣어주세요. 시각적으로 깔끔한 것도 좋지만, 포커스가 사라지면 사용자는 미아가 됩니다! 🧭♿ 제안 diff
<textarea id={noteId} value={note} onChange={(e) => onNoteChange?.(e.target.value)} placeholder="Please enter a note.." rows={1} - className="typo-body-r-12 text-timo-gray-700 placeholder:text-timo-gray-700 w-full resize-none bg-transparent outline-none" + className="typo-body-r-12 text-timo-gray-700 placeholder:text-timo-gray-700 focus-visible:ring-timo-blue-300 w-full resize-none bg-transparent outline-none focus-visible:ring-2" />As per path instructions,
outline: none으로 포커스 테두리 제거 금지 (**/*.{ts,tsx}접근성 규칙).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.<textarea id={noteId} value={note} onChange={(e) => onNoteChange?.(e.target.value)} placeholder="Please enter a note.." rows={1} className="typo-body-r-12 text-timo-gray-700 placeholder:text-timo-gray-700 focus-visible:ring-timo-blue-300 w-full resize-none bg-transparent outline-none focus-visible:ring-2" />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/app/`[locale]/(main)/home/_components/todo-create-modal/TodoCreateModal.tsx around lines 135 - 142, The TodoCreateModal textarea currently uses outline-none without any replacement focus indicator, which violates the accessibility rule. Update the textarea in TodoCreateModal to keep a visible keyboard focus state by adding a proper focus-visible style such as a ring or border while removing the raw outline suppression only if replaced. Make sure the change preserves clear focus feedback for the textarea element and follows the app’s accessibility convention for tsx components.Source: Path instructions
apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx (1)
68-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
함수 네이밍 컨벤션 살짝 어긋났어요! ✍️
isActivePath는 boolean을 반환하는 함수인데, 컨벤션은 함수 접두사로get/create/check/convert/filter만 허용하고 있습니다.checkIsActivePath또는 유사한 이름으로 바꾸는 게 일관성 있을 것 같아요.✏️ 제안
- const isActivePath = (pathname: string, href: string) => + const checkIsActivePath = (pathname: string, href: string) => pathname === href || pathname.startsWith(`${href}/`);(호출부
isActivePath(pathname, href)도 함께 이름 변경 필요)As per path instructions, "함수 접두사: get(반환) / create(생성) / check(확인) / convert(변환) / filter(필터링)".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const checkIsActivePath = (pathname: string, href: string) => pathname === href || pathname.startsWith(`${href}/`);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/timo-web/components/layout/sidebar/NavigationSidebar.tsx` around lines 68 - 69, Rename the boolean helper in NavigationSidebar from isActivePath to a convention-compliant name such as checkIsActivePath, and update every call site in the same component to use the new identifier. Keep the logic in the helper unchanged; this is only a naming consistency fix for the pathname/href active-state check.Source: Path instructions
NavigationSidebar의 하드코딩된 한국어 라벨을 messages/{en,ko}.json의
Navigation 네임스페이스로 대체하고, localePrefix를 always로 변경해
개발 중 /en, /ko 경로에 직접 접근할 수 있게 한다. 미사용 HomePage
번역 키 제거로 깨졌던 루트 page.tsx도 함께 정리한다.
jjangminii
left a comment
There was a problem hiding this comment.
다국어 버전 지원하는건 처음인데 넥스트에서 이렇게 처리할 수가 있네요-! 신기해요
다만 두 파일의 키 구조가 어긋나도 런타임 전까지 알 수 없어서요! next-intl의 TypeScript 타입 생성 기능을 활성화하면 컴파일 타임에 잡을 수 있을 것 같은데, 추가하는 게 어떨까요? 😊
관련 공식 문서: https://next-intl.dev/docs/workflows/typescript
There was a problem hiding this comment.
미들웨어 파일명이 proxy.ts인데, Next.js는 미들웨어를 middleware.ts로만 인식하는 걸로 알고 있어요. proxy.ts로는 실제로 미들웨어가 동작하지 않을 것 같아서요 middleware.ts로 rename이 필요하지 않을까요?
There was a problem hiding this comment.
middleware -> proxy로 변경이 되었다고 알고 있습니다!
좋은 정보 좋습니다! |
- next-intl의 AppConfig를 확장해 Locale과 Messages 타입을 선언했습니다 - 번역 키 자동완성과 컴파일 타임 타입 체크가 가능해졌습니다
…to feat/web/104-i18n-setup
- home 페이지 관련 컴포넌트·컨테이너·목업·타입·유틸을 (main) 하위에서 [locale]/(main) 하위로 이동했습니다 - 이동에 따라 변경된 절대 경로 import를 수정했습니다 - [locale] 라우트 그룹의 빈 플레이스홀더(.gitkeep) 파일을 제거했습니다
- [locale]/layout.tsx로 대체된 기존 root layout.tsx를 삭제했습니다
yumin-kim2
left a comment
There was a problem hiding this comment.
[locale] 세그먼트 도입과 라우트 그룹((main)) 병합이 자연스럽게 잘 되어있네용 👍
@/i18n/navigation으로 통일해서 링크 관련 이슈 미리 막아주신 부분도 너무 좋습니다!!
이런 다국어 지원은 기능은 처음 접해보는데 PR 설명이 꼼꼼하고 사용법 가이드도 남겨주셔서 편할 것 같아요 많은 도움이 됐습니다 고생하셨서요~~!!
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/timo-web/app/`[locale]/(main)/home/_mocks/todo-mock.ts:
- Line 1: The Todo import in the todo-mock module is type-only but is currently
written as a value import, so update the existing import to use type-only syntax
for consistency and better bundling. Keep the change localized to the Todo
import in todo-mock and match the pattern already used in HomeTodoContainer.tsx
by making the import explicitly type-only.
In `@apps/timo-web/i18n/routing.ts`:
- Line 6: The localePrefix setting in the routing configuration is still set to
always, which conflicts with the requirement to use as-needed from the start and
avoid an /en prefix on English routes. Update the localePrefix value in the
routing setup to as-needed and remove or revise the TODO comment so the behavior
matches the intended i18n routing policy.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 30874366-af13-4f86-bdc4-49dbeae62b3d
📒 Files selected for processing (14)
apps/timo-web/app/[locale]/(main)/home/_components/HomeDateInformation.tsxapps/timo-web/app/[locale]/(main)/home/_components/HomeTodoCard.tsxapps/timo-web/app/[locale]/(main)/home/_containers/HomeTodoContainer.tsxapps/timo-web/app/[locale]/(main)/home/_mocks/todo-mock.tsapps/timo-web/app/[locale]/(main)/home/_types/todo-type.tsapps/timo-web/app/[locale]/(main)/home/_utils/date.tsapps/timo-web/app/[locale]/(main)/home/_utils/todo-time.tsapps/timo-web/app/[locale]/(main)/home/page.tsxapps/timo-web/app/[locale]/page.tsxapps/timo-web/components/layout/sidebar/NavigationSidebar.tsxapps/timo-web/global.d.tsapps/timo-web/i18n/routing.tsapps/timo-web/messages/en.jsonapps/timo-web/messages/ko.json
| @@ -1,4 +1,4 @@ | |||
| import { Todo } from "@/app/(main)/home/_types/todo-type"; | |||
| import { Todo } from "@/app/[locale]/(main)/home/_types/todo-type"; | |||
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
타입 전용 import에는 import type을 붙여주세요.
Todo는 타입으로만 쓰이는데, 같은 계층의 HomeTodoContainer.tsx(Line 6)는 import type { Todo }를 쓰는 반면 여기는 값 import 구문을 그대로 쓰고 있어요. 사소하지만 번들 최적화와 일관성을 위해 맞춰주시면 좋을 것 같습니다.
💡 제안
-import { Todo } from "`@/app/`[locale]/(main)/home/_types/todo-type";
+import type { Todo } from "`@/app/`[locale]/(main)/home/_types/todo-type";관련 문서: TypeScript - import type
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { Todo } from "@/app/[locale]/(main)/home/_types/todo-type"; | |
| import type { Todo } from "`@/app/`[locale]/(main)/home/_types/todo-type"; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/timo-web/app/`[locale]/(main)/home/_mocks/todo-mock.ts at line 1, The
Todo import in the todo-mock module is type-only but is currently written as a
value import, so update the existing import to use type-only syntax for
consistency and better bundling. Keep the change localized to the Todo import in
todo-mock and match the pattern already used in HomeTodoContainer.tsx by making
the import explicitly type-only.
| export const routing = defineRouting({ | ||
| locales: ["en", "ko"], | ||
| defaultLocale: "en", | ||
| localePrefix: "always", // as-needed 로 추후 변경 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
localePrefix 설정이 PR 목표와 다릅니다 — TODO 코멘트 확인 필요.
주석에 as-needed 로 추후 변경이라고 적혀 있는데, 연동된 이슈 요구사항은 처음부터 en prefix 없이 as-needed로 동작해야 한다고 명시하고 있어요. 지금 always로 두면 /en/...처럼 영어 경로에도 항상 prefix가 붙어서 요구사항과 어긋납니다.
만약 스택의 다음 PR에서 바로 고칠 계획이라면 괜찮지만, 그렇지 않다면 지금 반영해주시는 게 좋을 것 같아요. 이 TODO, 제가 대신 처리해드릴까요? 아니면 이슈로 등록해드릴까요?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/timo-web/i18n/routing.ts` at line 6, The localePrefix setting in the
routing configuration is still set to always, which conflicts with the
requirement to use as-needed from the start and avoid an /en prefix on English
routes. Update the localePrefix value in the routing setup to as-needed and
remove or revise the TODO comment so the behavior matches the intended i18n
routing policy.
ISSUE 🔗
close #104
What is this PR? 🔍
timo-web에 next-intl 기반 다국어(영어/한국어) 지원을 위한 기반 라우팅·번역 설정을 구성했습니다.
배경
app/하위 라우트가 locale 구분 없이 단일 트리로 구성되어 있었고, 다국어 대응을 위한 번역 리소스나 라우팅 체계가 없었습니다.app/[locale]동적 세그먼트 하위로 이동하고, routing/navigation/request 설정과 middleware, 번역 메시지 파일을 추가했습니다.의존성
next-intl패키지를 의존성에 추가했습니다.i18n 라우팅 · 설정
i18n/routing.ts,i18n/navigation.ts,i18n/request.ts,proxy.ts(middleware),messages/en.json,messages/ko.json을 추가하고next.config.js에next-intl/plugin을 연결했습니다.routing), locale-aware 네비게이션 훅, 서버 요청별 locale/메시지 로딩 설정을 분리해서 구성하도록 요구합니다.routing.ts에서locales: ["en", "ko"],defaultLocale: "en",localePrefix: "always"를 정의해en,ko모두 URL에 프리픽스가 명시적으로 붙도록 했습니다(/en/...,/ko/...). 처음에는as-needed(기본 locale은 프리픽스 없음)로 구성했지만, 개발 중 두 로케일 경로 모두에 리다이렉트 없이 직접 접근해서 검증할 수 있어야 한다는 요구사항 때문에always로 변경했습니다.navigation.ts는 이routing설정을 기반으로Link,redirect,usePathname,useRouter,getPathname을 재노출해 이후 컴포넌트에서 locale-aware 네비게이션을 쓸 수 있게 했습니다.request.ts는hasLocale로 요청 locale을 검증하고, 유효하지 않으면defaultLocale로 폴백한 뒤messages/{locale}.json을 동적 import합니다.proxy.ts는next-intl/middleware의createMiddleware(routing)을 그대로 사용하고,matcher에서api,_next, 확장자가 있는 정적 파일 경로를 제외했습니다.Navigation네임스페이스:home/today/focus/statistics/settings)만 채워둔 상태이며, 나머지 페이지의 실제 텍스트를 메시지 키로 옮기는 작업은 이번 PR 범위 밖입니다.라우트 구조 이동
app/바로 아래 있던 라우트를app/[locale]/하위로 이동했습니다. 그중home,focus,today,statistics,settings(+account,policy)는 develop에 먼저 도입된(main)라우트 그룹(공용 사이드바 레이아웃)과 합쳐app/[locale]/(main)/하위로,login·onboarding은app/[locale]/바로 아래로 정리했습니다.[locale]동적 세그먼트 하위에 있어야 middleware가 해석한 locale을params.locale로 받을 수 있습니다.(main)그룹은 라우팅에 영향을 주지 않으면서 사이드바가 있는 페이지들만 묶어 레이아웃을 공유하기 위한 것이라,[locale]세그먼트 안쪽에 그대로 중첩하는 것이 맞습니다.app/[locale]/layout.tsx가 기존 root layout 역할을 이어받아generateStaticParams로routing.locales각각에 대한 정적 경로를 생성하고,params.locale이routing.locales에 없으면notFound()를 호출합니다.html lang속성도 이 locale 값을 사용하도록 했고,NextIntlClientProvider로 하위 트리를 감싸 클라이언트 컴포넌트에서useTranslations를 쓸 수 있게 했습니다. 기존app/layout.tsx,app/page.tsx는 역할이[locale]하위 파일로 옮겨졌으므로 제거했습니다. 폰트 파일(PretendardVariable.woff2)은[locale]세그먼트에 종속시키지 않기 위해apps/timo-web/fonts/(앱 루트)로 옮기고layout.tsx에서 상대 경로로 참조합니다.app/global-error.tsx는 Next.js 규칙상 root layout의 에러까지 잡아야 해서[locale]밖 최상위에 그대로 유지했습니다.[locale]세그먼트 바깥에는global-error.tsx와api라우트 정도만 남아 있어야 하며, 이번 PR에는 해당하는 다른 라우트가 없어 별도 조정은 없었습니다.사이드바 내비게이션 번역 적용
components/layout/sidebar/NavigationSidebar.tsx에서next/link,next/navigation의usePathname대신@/i18n/navigation의Link,usePathname을 사용하도록 바꾸고, 하드코딩돼 있던 한국어 라벨(홈,오늘,집중 모드,통계,설정)을useTranslations("Navigation")기반 번역으로 대체했습니다.localePrefix: "always"하에서next/link를 그대로 쓰면 현재 locale 프리픽스가 붙지 않아 링크를 클릭할 때 언어가 초기화되는 문제가 있어, locale-aware 버전으로 바꿔야 합니다. 라벨도 번역 없이 하드코딩돼 있으면en로케일에서도 한국어가 그대로 노출되므로 함께 고쳤습니다.import 경로 정리
i18n/navigation.ts,i18n/request.ts,proxy.ts에서routing을 가져오는 import를 상대 경로(./routing,./i18n/routing)에서@/i18n/routing절대 경로로 통일했습니다.@/절대 경로 alias 규칙을 그대로 따랐습니다.사용 방법 🛠️
다른 페이지·컴포넌트에서 이번에 추가된 i18n 설정을 실제로 어떻게 쓰면 되는지 정리했습니다.
1. 번역 텍스트 추가하기
messages/en.json,messages/ko.json에 같은 키 구조로 문구를 추가합니다. 페이지·기능 단위로 네임스페이스(최상위 키)를 나눠서 관리합니다. 실제로 지금 들어 있는Navigation네임스페이스가 예시입니다.두 파일의 키 구조가 어긋나면(한쪽에만 키가 있으면) 런타임에서 해당 언어로 봤을 때 문구가 비거나
네임스페이스.키형태의 텍스트가 그대로 노출될 수 있으니, 키를 추가할 때는 항상 두 파일에 동시에 반영합니다.2. 컴포넌트에서 번역 사용하기
서버 컴포넌트, 클라이언트 컴포넌트 모두
useTranslations를 그대로 쓸 수 있습니다. 네임스페이스(위에서 정한 최상위 키)를 인자로 넘기고,t("키")로 문구를 꺼냅니다.클라이언트 컴포넌트는
app/[locale]/layout.tsx의NextIntlClientProvider가 상위에서 감싸고 있어야 동작합니다. 현재 root layout에서 이미 전체 트리를 감싸고 있어 별도 설정 없이 바로 사용 가능합니다.3. 페이지 이동은
next/link,next/navigation대신@/i18n/navigation을 사용next/link의Link나next/navigation의useRouter,usePathname,redirect를 그대로 쓰면 현재 locale 프리픽스가 자동으로 붙지 않아 언어가 섞인 링크가 생길 수 있습니다. 대신i18n/navigation.ts에서 내보낸 버전을 사용합니다(NavigationSidebar.tsx가 실제 적용 예시입니다).4. 새 언어(locale) 추가하기
i18n/routing.ts의locales배열에 locale 코드를 추가합니다 (예:["en", "ko", "ja"]).messages/{locale}.json파일을 새로 만들고, 기존en.json과 동일한 키 구조로 번역을 채웁니다.defaultLocale을 바꾸지 않는 한 나머지 설정(navigation.ts,request.ts,proxy.ts)은 수정할 필요가 없습니다 — 모두routing을 참조하는 구조라 자동으로 새 locale을 인식합니다.5. URL 규칙
localePrefix: "always"설정/en,/en/settings,/ko,/ko/settings/,/settings)로 접속하면 미들웨어가 감지한 로케일(쿠키 →Accept-Language→defaultLocale순)로 리다이렉트됩니다. 리다이렉트 시NEXT_LOCALE쿠키가 내려가서, 이후 프리픽스 없는 경로로 다시 접속해도 사용자가 최근에 머문 로케일이 유지됩니다.새 페이지를 추가할 때 이 규칙을 그대로 따르면 되고, 별도로 언어별 라우트를 분기할 필요는 없습니다.
To Reviewers
라우트 이동은 위치 변경만 있고 페이지 컴포넌트 내부 로직은 그대로입니다(단,
NavigationSidebar의 링크·라벨 부분은 실제로 수정했습니다). 실제로 봐주셨으면 하는 부분은 다음과 같습니다.i18n/routing.ts의localePrefix: "always"선택이 프로젝트의 URL 정책과 맞는지 — 개발 편의(두 로케일 직접 접근)를 위해as-needed에서 바꿨는데, 프로덕션에서도 이 정책을 유지할지 논의가 필요합니다.[locale]/layout.tsx의notFound()폴백 방식이 기대하는 동작인지.app/[locale]/(main)/구조로 develop의 라우트 그룹과 합친 방식이 맞는지,NavigationSidebar의 링크가@/i18n/navigation기반으로 바뀐 부분이 맞는지.messages/*.json에는 아직 사이드바 라벨(Navigation)만 채워져 있고, 나머지 페이지의 텍스트를 번역 키로 옮기는 작업은 후속 PR로 남겨두었습니다.Screenshot 📷
Test Checklist ✔
pnpm check-types통과pnpm lint통과pnpm build통과/en,/ko등 locale 라우팅 동작 확인 (curl로 리다이렉트·쿠키 동작 검증)