UI english versions - #104
Merged
Merged
Conversation
Move every user-visible Korean string into src/locales (ko.ts verbatim, en.ts translated). The locale is chosen once at page load (localStorage rc_lang override, else browser language) and exported as L; components read plain constants — no i18n library, no runtime lookup. ko.ts is typed against en.ts's shape so the two languages cannot drift apart silently. Parameterized strings are per-language functions so Korean counters and English word order each read naturally. A globe LanguageToggle (labeled with the language it switches to) sits in both navbars — signed-in and signed-out — and persists + reloads. Tests are pinned to Korean in setup.ts since the suite asserts the original copy; locales.test.ts covers the English side and structural parity. Dev-only fixtures (UITestPage, teamsDummyData) and code comments intentionally keep their Korean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Normalize brand 'Rune'/'rune' → 'RUNE' in the en/ko locale tables (account-required, invite status, update notice). Technical identifiers are unaffected (none present in the locale files).
Drive language state with plain i18next; L becomes a Proxy over the active locale table so all L.x.y call sites and the compile-time-typed tables are unchanged. A LocaleProvider subscribes to i18next's languageChanged and remounts its subtree, so switching re-renders in place instead of window.location.reload() — removing the switch flash. setLanguage calls changeLanguage (keeps localStorage persistence); LanguageToggle reads the live language via getLanguage(). Locale test compares L by structure since it is now a Proxy.
CI installs with --frozen-lockfile; add the i18next entry so the lockfile matches package.json.
Contributor
|
When toggling the language, things like table header text, team info change but the member status badge, button text,... don't updated. It changes after refreshing the window |
Module-scope constants that captured L.* froze at the load-time language,
because LocaleProvider's key={lang} remount re-runs render but not module
top-level code. Make the shared containers resolve per-access and inline
the rest at their render sites:
- styleConstants: MEMBER/INVITATION/WORKSPACE_STATUS_VAR labels via a
statusVar() getter helper (status badges)
- commonConstants: BTN_TEXT/MODAL_TITLES as forwarding proxies over
L.btn/L.modal; NAV_LIST titles as getters (buttons, modal titles, nav)
- inline L.* at render sites for team-name hint, invite/delete/role/
membership messages, username error; batchReason() helper in the drawer
Phase 1 of the i18n re-integration. release/v1.0.6 refactored the same components this branch localized (decomposed drawers/pages into hooks+ views, unified forked modals, centralized copy into constants), so the 28 conflicts were resolved by taking release's version wholesale — the newer architecture is the baseline. The i18n-only files (locales/en.ts, ko.ts, index.ts, LocaleProvider, LanguageToggle) are non-conflicting and survive. Two forked modals release deleted (teams/RemoveMembershipModal, teams/RoleChangeConfirmModal) are dropped to match. Phase 2 (follow-up commit) re-wires the i18n layer onto release's centralized copy and localizes the new hook/view files. The tree is intentionally un-localized (release Korean) at this commit.
Re-integrate the i18n layer onto release/v1.0.6's architecture, starting with the highest-leverage surface — the centralized copy constants, which cascade to every screen: - mount LanguageToggle in the release Navbar; localize the workspace badge - commonConstants: BTN_TEXT, PAGE_TITLES, MODAL_TITLES, TABLE_HEADERS, INPUT_LABELS, PLACEHOLDERS, ARIA_LABELS, FEEDBACK_TEXT, NAV_LIST → live getters/functions over L.* - noticeConstants, errorConstants (computed-key getters), styleConstants (status badges via statusVar getters) → live over L.* - teamConstants/email/username bare exports inlined at call sites (live) All copy re-resolves on a live switch (getters/proxies), so no reload flash. Component-level inline strings (pages, modal bodies) are the remaining pass. tsc clean, 333/333 tests pass, build succeeds.
Complete the re-integration: every user-visible Korean string in the components/pages now reads from the locale tables, so a live language switch turns the entire UI English (not just the shared chrome). - 25 files auto-localized (JSX text, attributes, string props) via the Korean→key reverse index from ko.ts - interpolated strings wired to the locale's function keys (footers, aria-labels, confirm-dialog bodies, member-count headings, team meta, invite notices with the embedded status chip) - inlined the remaining frozen module-const message captures so they re-resolve live (send/remove/role/delete failure copy) Only code comments and regex character classes retain Korean. tsc clean, 333/333 tests pass, build succeeds.
The Users and Sessions filter/sort option arrays were module-scope constants whose labels read L.* / getters — so they froze at the load-time language and the dropdowns stayed in that language after a live switch (the rest of the page switched, leaving the page looking half-translated). Convert them to builder functions called in the component body so they re-resolve on every render. tsc clean, 333/333 tests pass, build succeeds.
The mock server seeds English sample team names (Platform, Data, …). Add a demo-only L.demoTeams map + localizeTeamName() so those sample names follow the language toggle on the Team Management surfaces (tree, org chart, team card, pickers). Real, customer-created names are not in the map and render verbatim — team names are user data and must never be auto-translated in production; drop this map when the app talks to a real backend. Also aligns nav.sessions to the wireframe (세션 관리 / Session Management). The delete-confirm tests type the displayed (localized) name. tsc clean, 333/333 tests pass, build succeeds.
geuna0204
approved these changes
Aug 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the RUNE Console fully bilingual (Korean/English) with an in-navbar language toggle. Korean copy is unchanged; English is new. Three layers of work, in three commits.
Base branch: release/v1.0.6. ~47 frontend files, +1,398/−421. No backend/Go changes.
What's in it
Bilingual UI — static typed locale tables (85c50fa)
All user-facing Korean strings moved out of components into two static, compile-time-typed TypeScript tables (src/locales/en.ts / ko.ts), read through a single L accessor. ko.ts is typed as TLocale = typeof en, so a key added to one language and missing from the other is a build error — the two can't silently drift. Every component reads L.x.y; a globe LanguageToggle lives in the navbar. Tests are pinned to Korean in test/setup.ts (the suite asserts the original copy).
Brand casing standardization (dbd9141)
Normalized all brand mentions to all-caps RUNE in the locale tables (en.ts + ko.ts), per review. Technical identifiers are unaffected.
i18next live language switching — no reload flash (3e5771c)
Replaced reload-on-switch with in-place switching:
이중 언어(Bilingual) UI — 정적 타입 로케일 테이블 (85c50fa)
사용자에게 노출되는 모든 한국어 문자열을 컴포넌트에서 분리하여, 단일 L 접근자(accessor)를 통해 읽을 수 있는 두 개의 컴파일 타임 정적 타입 TypeScript 테이블(src/locales/en.ts 및 ko.ts)로 이동했습니다. ko.ts는 TLocale = typeof en으로 타입이 지정되어, 한 언어에만 키가 추가되고 다른 언어에 누락될 경우 빌드 에러가 발생합니다. 따라서 두 언어 파일 간의 싱크가 어긋나는 것을 원천적으로 방지합니다. 모든 컴포넌트는 L.x.y 형태로 문자열을 참조하며, 네비게이션 바에는 지구본 모양의 언어 전환 토글(LanguageToggle)을 추가했습니다. 테스트 환경(test/setup.ts)은 한국어로 고정하여 기존 테스트 코드들이 원본 텍스트를 정상적으로 검증할 수 있도록 했습니다.
브랜드명 대소문자 표기 표준화 (dbd9141)
코드 리뷰 피드백을 반영하여 로케일 테이블(en.ts 및 ko.ts) 내의 모든 브랜드 명칭을 대문자 'RUNE'으로 통일했습니다. 코드 내 기술적인 식별자(변수명 등)는 영향을 받지 않습니다.
i18next 실시간 언어 전환 — 새로고침 깜빡임 제거 (3e5771c)
언어 변경 시 페이지 전체가 새로고침되던 방식을 즉시 반영(in-place switching)되도록 개선했습니다:
기본 i18next가 언어 상태를 관리하도록 구성했습니다. L 객체는 현재 활성화된 언어 테이블을 감싸는 Proxy로 동작하므로, 기존의 모든 L.x.y 호출부와 타입 정의 테이블을 수정 없이 그대로 유지할 수 있었습니다 (i18next의 기본 JSON 카탈로그 방식을 사용했다면 컴파일 타임 타입 안정성을 잃었을 것입니다).
LocaleProvider가 i18next의 languageChanged 이벤트를 구독하여 하위 트리를 리마운트(remount)합니다. 이를 통해 window.location.reload() 없이 화면이 제자리에서 리렌더링되어 언어 전환 시 발생하던 화면 깜빡임 현상이 사라졌습니다.
setLanguage 함수는 내부적으로 changeLanguage를 호출하여 로컬 스토리지(localStorage) 저장 상태를 유지하며, LanguageToggle 컴포넌트는 getLanguage()를 통해 현재 적용된 언어를 실시간으로 읽어옵니다