Apply Language Settings
This commit is contained in:
1 parent
39113a4d39
commit
5dfb1dba03
11 files changed
+296
-27
No files matched your search
@@ -0,0 +1,98 @@
|
||||
import { Injectable, signal, inject, PLATFORM_ID, computed, InjectionToken } from '@angular/core';
|
||||
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
|
||||
|
||||
// Angular 21 SSR 환경에서 서버 요청 객체를 가져오기 위한 토큰 정의
|
||||
export const REQUEST = new InjectionToken<any>('REQUEST');
|
||||
|
||||
export type Language = 'EN' | 'KR' | 'JP';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SettingsService {
|
||||
private platformId = inject(PLATFORM_ID);
|
||||
|
||||
// 서버 사이드에서만 주입되는 Request 객체를 가져옵니다.
|
||||
private request = inject(REQUEST, { optional: true });
|
||||
|
||||
// Signals for state
|
||||
private _language = signal<Language>('EN');
|
||||
private _darkMode = signal<boolean>(true);
|
||||
private _romanisation = signal<boolean>(false);
|
||||
private _userName = signal<string>('Guest');
|
||||
|
||||
// Public computed or read-only signals
|
||||
language = computed(() => this._language());
|
||||
darkMode = computed(() => this._darkMode());
|
||||
romanisation = computed(() => this._romanisation());
|
||||
userName = computed(() => this._userName());
|
||||
|
||||
constructor() {
|
||||
this.initializeSettings();
|
||||
}
|
||||
|
||||
private initializeSettings() {
|
||||
if (isPlatformBrowser(this.platformId)) {
|
||||
// 클라이언트: 브라우저 API 사용
|
||||
this._language.set((this.getCookie('lang') as Language) || 'EN');
|
||||
this._darkMode.set(this.getCookie('darkMode') !== 'false');
|
||||
this._romanisation.set(this.getCookie('romanisation') === 'true');
|
||||
this._userName.set(this.getCookie('userName') || 'Guest');
|
||||
} else if (isPlatformServer(this.platformId)) {
|
||||
// 서버: 요청 객체의 쿠키 읽기
|
||||
// Express 요청 객체의 경우 headers['cookie']를 사용합니다.
|
||||
const cookieHeader = (this.request as any)?.headers?.['cookie'] || (this.request as any)?.headers?.cookie;
|
||||
if (cookieHeader) {
|
||||
const cookies = this.parseServerCookies(cookieHeader);
|
||||
if (cookies['lang']) this._language.set(cookies['lang'] as Language);
|
||||
if (cookies['darkMode'] !== undefined) this._darkMode.set(cookies['darkMode'] !== 'false');
|
||||
if (cookies['romanisation'] !== undefined) this._romanisation.set(cookies['romanisation'] === 'true');
|
||||
if (cookies['userName']) this._userName.set(cookies['userName']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private parseServerCookies(cookieString: string): Record<string, string> {
|
||||
const cookies: Record<string, string> = {};
|
||||
cookieString.split(';').forEach(c => {
|
||||
const [key, value] = c.trim().split('=');
|
||||
if (key && value) cookies[key] = decodeURIComponent(value);
|
||||
});
|
||||
return cookies;
|
||||
}
|
||||
|
||||
setLanguage(lang: Language) {
|
||||
this._language.set(lang);
|
||||
this.setCookie('lang', lang);
|
||||
}
|
||||
|
||||
setDarkMode(isDark: boolean) {
|
||||
this._darkMode.set(isDark);
|
||||
this.setCookie('darkMode', isDark.toString());
|
||||
}
|
||||
|
||||
setRomanisation(value: boolean) {
|
||||
this._romanisation.set(value);
|
||||
this.setCookie('romanisation', value.toString());
|
||||
}
|
||||
|
||||
setUserName(name: string) {
|
||||
this._userName.set(name);
|
||||
this.setCookie('userName', name);
|
||||
}
|
||||
|
||||
private getCookie(name: string): string | undefined {
|
||||
if (isPlatformBrowser(this.platformId)) {
|
||||
const value = `; ${document.cookie}`;
|
||||
const parts = value.split(`; ${name}=`);
|
||||
if (parts.length === 2) return parts.pop()?.split(';').shift();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private setCookie(name: string, value: string) {
|
||||
if (isPlatformBrowser(this.platformId)) {
|
||||
document.cookie = `${name}=${value}; path=/; max-age=31536000`; // 1 year
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { inject, Injectable, computed } from '@angular/core';
|
||||
import { SettingsService } from './settings.service';
|
||||
import { EN, Translation } from '../i18n/en';
|
||||
import { KR } from '../i18n/kr';
|
||||
import { JP } from '../i18n/jp';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TranslationService {
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
/**
|
||||
* 내부 시그널: 실제 번역 데이터 객체
|
||||
*/
|
||||
private _translation = computed(() => {
|
||||
const lang = this.settingsService.language();
|
||||
switch (lang) {
|
||||
case 'KR': return KR;
|
||||
case 'JP': return JP;
|
||||
default: return EN;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Proxy를 통한 깔끔한 접근용 객체.
|
||||
* {{ t.settings.title }} 처럼 () 없이 접근해도 내부적으로 _t() 시그널을 읽습니다.
|
||||
*/
|
||||
readonly translation = new Proxy({} as Translation, {
|
||||
get: (_, prop) => this._translation()[prop as keyof Translation]
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user