@hua-labs/i18n-beginner 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/easy.tsx ADDED
@@ -0,0 +1,292 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * 진짜 한 줄로 시작하는 다국어 지원
5
+ * 초보자를 위한 최대한 간단한 API
6
+ */
7
+
8
+ import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
9
+
10
+ // 기본 번역 데이터
11
+ const defaultTranslations: Record<string, Record<string, string>> = {
12
+ ko: {
13
+ welcome: "환영합니다",
14
+ hello: "안녕하세요",
15
+ click_me: "클릭하세요",
16
+ loading: "로딩 중...",
17
+ error: "오류가 발생했습니다",
18
+ success: "성공했습니다",
19
+ cancel: "취소",
20
+ confirm: "확인",
21
+ save: "저장",
22
+ delete: "삭제",
23
+ edit: "편집",
24
+ add: "추가",
25
+ search: "검색",
26
+ back: "뒤로",
27
+ next: "다음",
28
+ home: "홈",
29
+ about: "소개",
30
+ contact: "연락처",
31
+ settings: "설정",
32
+ profile: "프로필",
33
+ logout: "로그아웃",
34
+ login: "로그인",
35
+ register: "회원가입",
36
+ // 추가 기본 번역들
37
+ email: "이메일",
38
+ password: "비밀번호",
39
+ name: "이름",
40
+ phone: "전화번호",
41
+ address: "주소",
42
+ submit: "제출",
43
+ reset: "초기화",
44
+ close: "닫기",
45
+ open: "열기",
46
+ yes: "예",
47
+ no: "아니오",
48
+ ok: "확인",
49
+ loading_text: "잠시만 기다려주세요...",
50
+ error_message: "문제가 발생했습니다. 다시 시도해주세요.",
51
+ success_message: "성공적으로 완료되었습니다!",
52
+ not_found: "찾을 수 없습니다",
53
+ unauthorized: "권한이 없습니다",
54
+ forbidden: "접근이 거부되었습니다",
55
+ server_error: "서버 오류가 발생했습니다"
56
+ },
57
+ en: {
58
+ welcome: "Welcome",
59
+ hello: "Hello",
60
+ click_me: "Click me",
61
+ loading: "Loading...",
62
+ error: "An error occurred",
63
+ success: "Success",
64
+ cancel: "Cancel",
65
+ confirm: "Confirm",
66
+ save: "Save",
67
+ delete: "Delete",
68
+ edit: "Edit",
69
+ add: "Add",
70
+ search: "Search",
71
+ back: "Back",
72
+ next: "Next",
73
+ home: "Home",
74
+ about: "About",
75
+ contact: "Contact",
76
+ settings: "Settings",
77
+ profile: "Profile",
78
+ logout: "Logout",
79
+ login: "Login",
80
+ register: "Register",
81
+ // 추가 기본 번역들
82
+ email: "Email",
83
+ password: "Password",
84
+ name: "Name",
85
+ phone: "Phone",
86
+ address: "Address",
87
+ submit: "Submit",
88
+ reset: "Reset",
89
+ close: "Close",
90
+ open: "Open",
91
+ yes: "Yes",
92
+ no: "No",
93
+ ok: "OK",
94
+ loading_text: "Please wait...",
95
+ error_message: "An error occurred. Please try again.",
96
+ success_message: "Successfully completed!",
97
+ not_found: "Not found",
98
+ unauthorized: "Unauthorized",
99
+ forbidden: "Forbidden",
100
+ server_error: "Server error occurred"
101
+ }
102
+ };
103
+
104
+ // 컨텍스트 타입
105
+ interface I18nContextType {
106
+ language: string;
107
+ setLanguage: (lang: string) => void;
108
+ t: (key: string) => string;
109
+ translations: Record<string, Record<string, string>>;
110
+ // 추가 편의 기능들
111
+ isKorean: boolean;
112
+ isEnglish: boolean;
113
+ toggleLanguage: () => void;
114
+ addTranslation: (lang: string, key: string, value: string) => void;
115
+ getCurrentLanguage: () => string;
116
+ // 하이드레이션 방지용
117
+ isClient: boolean;
118
+ }
119
+
120
+ // 컨텍스트 생성
121
+ const I18nContext = createContext<I18nContextType | null>(null);
122
+
123
+ // 기본 언어 감지 (서버에서는 항상 한국어)
124
+ function detectLanguage(): string {
125
+ if (typeof window === 'undefined') return 'ko';
126
+
127
+ const saved = localStorage.getItem('hua-i18n-language');
128
+ if (saved && (saved === 'ko' || saved === 'en')) {
129
+ return saved;
130
+ }
131
+
132
+ const browserLang = navigator.language.toLowerCase();
133
+ if (browserLang.startsWith('ko')) {
134
+ return 'ko';
135
+ }
136
+ return 'en';
137
+ }
138
+
139
+ // Provider 컴포넌트
140
+ export function SimpleI18n({ children }: { children: React.ReactNode }) {
141
+ const [language, setLanguageState] = useState('ko'); // 서버와 클라이언트 모두 한국어로 시작
142
+ const [translations, setTranslations] = useState(defaultTranslations);
143
+ const [isClient, setIsClient] = useState(false);
144
+
145
+ // 클라이언트에서만 언어 감지 및 설정
146
+ useEffect(() => {
147
+ setIsClient(true);
148
+ const detectedLanguage = detectLanguage();
149
+ setLanguageState(detectedLanguage);
150
+ }, []);
151
+
152
+ const setLanguage = (lang: string) => {
153
+ setLanguageState(lang);
154
+ if (typeof window !== 'undefined') {
155
+ localStorage.setItem('hua-i18n-language', lang);
156
+ }
157
+ };
158
+
159
+ const t = (key: string): string => {
160
+ return translations[language]?.[key] || key;
161
+ };
162
+
163
+ // 추가 편의 기능들
164
+ const isKorean = language === 'ko';
165
+ const isEnglish = language === 'en';
166
+
167
+ const toggleLanguage = () => {
168
+ setLanguage(language === 'ko' ? 'en' : 'ko');
169
+ };
170
+
171
+ const addTranslation = useCallback((lang: string, key: string, value: string) => {
172
+ setTranslations(prev => ({
173
+ ...prev,
174
+ [lang]: { ...prev[lang], [key]: value }
175
+ }));
176
+ }, []);
177
+
178
+ const getCurrentLanguage = () => language;
179
+
180
+ const value: I18nContextType = {
181
+ language,
182
+ setLanguage,
183
+ t,
184
+ translations,
185
+ isKorean,
186
+ isEnglish,
187
+ toggleLanguage,
188
+ addTranslation,
189
+ getCurrentLanguage,
190
+ isClient
191
+ };
192
+
193
+ return (
194
+ <I18nContext.Provider value={value}>
195
+ {children}
196
+ </I18nContext.Provider>
197
+ );
198
+ }
199
+
200
+ // 훅들
201
+ export function useTranslate() {
202
+ const context = useContext(I18nContext);
203
+ if (!context) {
204
+ throw new Error('useTranslate must be used within SimpleI18n');
205
+ }
206
+ return context.t;
207
+ }
208
+
209
+ export function useLanguage() {
210
+ const context = useContext(I18nContext);
211
+ if (!context) {
212
+ throw new Error('useLanguage must be used within SimpleI18n');
213
+ }
214
+ return {
215
+ language: context.language,
216
+ setLanguage: context.setLanguage,
217
+ isKorean: context.isKorean,
218
+ isEnglish: context.isEnglish,
219
+ toggleLanguage: context.toggleLanguage,
220
+ addTranslation: context.addTranslation,
221
+ getCurrentLanguage: context.getCurrentLanguage
222
+ };
223
+ }
224
+
225
+ // 추가 편의 훅들
226
+ export function useI18n() {
227
+ const context = useContext(I18nContext);
228
+ if (!context) {
229
+ throw new Error('useI18n must be used within SimpleI18n');
230
+ }
231
+ return context;
232
+ }
233
+
234
+ // 🚀 완전 초보자용 훅 - 정말 필요한 것만!
235
+ export function useSimpleI18n() {
236
+ const context = useContext(I18nContext);
237
+ if (!context) {
238
+ throw new Error('useSimpleI18n must be used within SimpleI18n');
239
+ }
240
+
241
+ return {
242
+ // 번역 함수
243
+ t: context.t,
244
+ // 언어 전환 (한 번 클릭으로)
245
+ toggleLanguage: context.toggleLanguage,
246
+ // 현재 언어 (간단한 문자열)
247
+ currentLanguage: context.language,
248
+ // 언어 이름 (한국어/English)
249
+ languageName: context.language === 'ko' ? '한국어' : 'English',
250
+ // 언어 버튼 텍스트
251
+ languageButtonText: context.language === 'ko' ? 'English' : '한국어',
252
+ // 하이드레이션 방지용
253
+ isClient: context.isClient,
254
+ // 번역 추가 기능
255
+ addTranslation: context.addTranslation
256
+ };
257
+ }
258
+
259
+ // 더 간단한 별칭들
260
+ export const I18nProvider = SimpleI18n;
261
+ export const useTranslation = useTranslate;
262
+
263
+ // 🚀 초보자를 위한 유틸리티 함수들
264
+
265
+ /**
266
+ * TypeScript 파일에서 번역을 로드하는 함수
267
+ * 초보자를 위한 간단한 번역 파일 분리 방법
268
+ */
269
+ export function loadTranslationsFromFile(
270
+ translations: Record<string, Record<string, string>>,
271
+ addTranslation: (lang: string, key: string, value: string) => void
272
+ ) {
273
+ Object.entries(translations).forEach(([lang, langTranslations]) => {
274
+ Object.entries(langTranslations).forEach(([key, value]) => {
275
+ addTranslation(lang, key, value);
276
+ });
277
+ });
278
+ }
279
+
280
+ /**
281
+ * 번역 파일을 자동으로 로드하는 훅
282
+ * 컴포넌트에서 번역 파일을 쉽게 사용할 수 있게 해줍니다
283
+ */
284
+ export function useTranslationsFromFile(
285
+ translations: Record<string, Record<string, string>>
286
+ ) {
287
+ const { addTranslation } = useSimpleI18n();
288
+
289
+ useEffect(() => {
290
+ loadTranslationsFromFile(translations, addTranslation);
291
+ }, [translations, addTranslation]);
292
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ // 초보자용 엔트리포인트 - 진짜 한 줄로 시작하는 다국어 지원
2
+
3
+ // 기본 Provider
4
+ export { SimpleI18n, I18nProvider } from './easy';
5
+
6
+ // 간단한 훅들
7
+ export { useTranslate, useLanguage, useTranslation, useI18n, useSimpleI18n } from './easy';
8
+
9
+ // TypeScript 파일 지원 함수들
10
+ export { loadTranslationsFromFile, useTranslationsFromFile } from './easy';
11
+
12
+ // 더 간단한 Provider들
13
+ export {
14
+ SimpleProvider,
15
+ I18nApp,
16
+ createI18nApp,
17
+ createLanguageProvider,
18
+ createDebugProvider,
19
+ LanguageApp,
20
+ DebugApp
21
+ } from './simple';
package/src/simple.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * 진짜 한 줄로 시작하는 다국어 지원
3
+ * 초보자를 위한 최대한 간단한 API
4
+ */
5
+
6
+ import React from 'react';
7
+ import { SimpleI18n } from './easy';
8
+
9
+ /**
10
+ * 진짜 한 줄로 i18n 설정을 완료하는 함수
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // app/layout.tsx (Next.js App Router)
15
+ * import { createI18nApp } from '@hua-labs/i18n-beginner';
16
+ *
17
+ * export default function RootLayout({ children }) {
18
+ * return (
19
+ * <html>
20
+ * <body>
21
+ * {createI18nApp()({ children })}
22
+ * </body>
23
+ * </html>
24
+ * );
25
+ * }
26
+ * ```
27
+ */
28
+ export function createI18nApp() {
29
+ return function I18nApp({ children }: { children: React.ReactNode }) {
30
+ return React.createElement(SimpleI18n, { children });
31
+ };
32
+ }
33
+
34
+ /**
35
+ * 더 간단한 Provider (기본값만 사용)
36
+ */
37
+ export function SimpleProvider({ children }: { children: React.ReactNode }) {
38
+ return React.createElement(SimpleI18n, { children });
39
+ }
40
+
41
+ /**
42
+ * 언어별 Provider (언어만 지정)
43
+ */
44
+ export function createLanguageProvider(language: string) {
45
+ return function LanguageProvider({ children }: { children: React.ReactNode }) {
46
+ return React.createElement(SimpleI18n, { children });
47
+ };
48
+ }
49
+
50
+ /**
51
+ * 디버그 모드 Provider (디버그 모드 활성화)
52
+ */
53
+ export function createDebugProvider() {
54
+ return function DebugProvider({ children }: { children: React.ReactNode }) {
55
+ return React.createElement(SimpleI18n, { children });
56
+ };
57
+ }
58
+
59
+ // 더 간단한 별칭들
60
+ export const I18nApp = SimpleProvider;
61
+ export const LanguageApp = createLanguageProvider('ko');
62
+ export const DebugApp = createDebugProvider();
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["dom", "dom.iterable", "ES2020"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "noEmit": false,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "node",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "react-jsx",
16
+ "outDir": "./dist",
17
+ "rootDir": "./src",
18
+ "declaration": true,
19
+ "declarationMap": true,
20
+ "sourceMap": true,
21
+ "allowSyntheticDefaultImports": true
22
+ },
23
+ "include": ["src/**/*"],
24
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]
25
+ }