@meursyphus/i18n-llm 0.0.1

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/dist/client.js ADDED
@@ -0,0 +1,86 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/client/use-translations.ts
5
+ import { useContext } from "react";
6
+
7
+ // src/client/translations-context.tsx
8
+ import { createContext } from "react";
9
+ import { jsx } from "react/jsx-runtime";
10
+ var TranslationsContext = createContext({
11
+ messages: {},
12
+ locale: ""
13
+ });
14
+ function ClientTranslationsProvider({
15
+ messages,
16
+ locale,
17
+ children
18
+ }) {
19
+ return /* @__PURE__ */ jsx(TranslationsContext.Provider, { value: { messages, locale }, children });
20
+ }
21
+
22
+ // src/shared/interpolate.ts
23
+ function interpolate(template, variables) {
24
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
25
+ if (Object.prototype.hasOwnProperty.call(variables, key)) {
26
+ return String(variables[key]);
27
+ }
28
+ return match;
29
+ });
30
+ }
31
+
32
+ // src/shared/nested-key-utils.ts
33
+ function getNestedValue(obj, path) {
34
+ const keys = path.split(".");
35
+ let value = obj;
36
+ for (const key of keys) {
37
+ if (value === null || value === void 0) return void 0;
38
+ value = value[key];
39
+ }
40
+ return value;
41
+ }
42
+ function isReactNode(value) {
43
+ return value !== null && value !== void 0 && (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "object" && value.$$typeof !== void 0);
44
+ }
45
+
46
+ // src/client/use-translations.ts
47
+ function useTranslations(namespace) {
48
+ const { messages } = useContext(TranslationsContext);
49
+ function t(key, variables) {
50
+ const namespaceMessages = messages[namespace];
51
+ const value = getNestedValue(namespaceMessages, key);
52
+ if (value === void 0 || value === null) {
53
+ console.warn(
54
+ `i18n-llm: Translation for key "${String(namespace)}.${key}" not found.`
55
+ );
56
+ return key;
57
+ }
58
+ if (typeof value === "string") {
59
+ if (variables) {
60
+ return interpolate(value, variables);
61
+ }
62
+ return value;
63
+ }
64
+ if (isReactNode(value)) {
65
+ return value;
66
+ }
67
+ console.warn(
68
+ `i18n-llm: Translation for key "${String(namespace)}.${key}" is not a valid type.`
69
+ );
70
+ return key;
71
+ }
72
+ return t;
73
+ }
74
+
75
+ // src/client/use-current-language.ts
76
+ import { useContext as useContext2 } from "react";
77
+ function useCurrentLanguage() {
78
+ const { locale } = useContext2(TranslationsContext);
79
+ return locale;
80
+ }
81
+ export {
82
+ ClientTranslationsProvider,
83
+ TranslationsContext,
84
+ useCurrentLanguage,
85
+ useTranslations
86
+ };
@@ -0,0 +1,29 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ interface I18nConfig {
4
+ defaultLocale: string;
5
+ locales: readonly string[];
6
+ messagesPath: string;
7
+ }
8
+ type Messages = Record<string, unknown>;
9
+ type NestedKeyOf<TObj extends object> = {
10
+ [Key in keyof TObj & (string | number)]: TObj[Key] extends object ? `${Key}` | `${Key}.${NestedKeyOf<TObj[Key]>}` : `${Key}`;
11
+ }[keyof TObj & (string | number)];
12
+ type NestedValueOf<TObj extends object, TPath extends string> = TPath extends `${infer Key}.${infer Rest}` ? Key extends keyof TObj ? TObj[Key] extends object ? NestedValueOf<TObj[Key], Rest> : never : never : TPath extends keyof TObj ? TObj[TPath] : never;
13
+ type InterpolationVariables = Record<string, string | number>;
14
+ type TranslationValue = string | ReactNode;
15
+ interface TranslationsContextType<TMessages = Messages> {
16
+ messages: TMessages;
17
+ locale: string;
18
+ }
19
+ interface MiddlewareConfig {
20
+ locales: string[];
21
+ defaultLocale: string;
22
+ localePrefix?: "always" | "as-needed" | "never";
23
+ redirects?: Array<{
24
+ from: string;
25
+ to: string;
26
+ }>;
27
+ }
28
+
29
+ export type { InterpolationVariables as I, Messages as M, NestedKeyOf as N, TranslationValue as T, NestedValueOf as a, I18nConfig as b, MiddlewareConfig as c, TranslationsContextType as d };
@@ -0,0 +1,99 @@
1
+ import { M as Messages, N as NestedKeyOf, I as InterpolationVariables, a as NestedValueOf, b as I18nConfig } from './index-KVK-m3p1.js';
2
+ export { c as MiddlewareConfig, T as TranslationValue, d as TranslationsContextType } from './index-KVK-m3p1.js';
3
+ import { ReactNode } from 'react';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { NextRequest } from 'next/server';
6
+
7
+ /**
8
+ * Gets translations for a specific namespace in server components.
9
+ *
10
+ * @example
11
+ * // In a Server Component
12
+ * const t = await getTranslations('common', lang);
13
+ * return <h1>{t('title')}</h1>;
14
+ *
15
+ * // With variables
16
+ * return <p>{t('greeting', { name: 'World' })}</p>;
17
+ */
18
+ declare function getTranslations<TMessages extends Messages, Namespace extends keyof TMessages>(namespace: Namespace, locale: string): Promise<(<Key extends NestedKeyOf<TMessages[Namespace] & object>>(key: Key, variables?: InterpolationVariables) => NestedValueOf<TMessages[Namespace] & object, Key> extends string ? string : NestedValueOf<TMessages[Namespace] & object, Key> extends ReactNode ? ReactNode : string)>;
19
+
20
+ interface TranslationsProviderProps {
21
+ children: ReactNode;
22
+ locale: string;
23
+ }
24
+ /**
25
+ * Server-side translations provider.
26
+ * Loads translations and passes them to the client context.
27
+ *
28
+ * @example
29
+ * // In your root layout
30
+ * export default async function RootLayout({ children, params }) {
31
+ * const { lang } = await params;
32
+ * return (
33
+ * <html lang={lang}>
34
+ * <body>
35
+ * <TranslationsProvider locale={lang}>
36
+ * {children}
37
+ * </TranslationsProvider>
38
+ * </body>
39
+ * </html>
40
+ * );
41
+ * }
42
+ */
43
+ declare function TranslationsProvider<TMessages extends Messages = Messages>({ children, locale }: TranslationsProviderProps): Promise<react_jsx_runtime.JSX.Element>;
44
+
45
+ /**
46
+ * Gets the user's preferred language from the request.
47
+ * Checks in order: cookie preference, Accept-Language header, default locale.
48
+ */
49
+ declare function getPreferredLanguage(request: NextRequest): string;
50
+
51
+ /**
52
+ * Sets the i18n configuration for message loading.
53
+ * This should be called once at application startup.
54
+ */
55
+ declare function setI18nConfig(config: {
56
+ defaultLocale: string;
57
+ locales: string[];
58
+ messagesPath: string;
59
+ }): void;
60
+ /**
61
+ * Gets the current i18n configuration.
62
+ */
63
+ declare function getI18nConfig(): {
64
+ defaultLocale: string;
65
+ locales: string[];
66
+ messagesPath: string;
67
+ } | null;
68
+ /**
69
+ * Loads translation messages for a specific locale.
70
+ * Falls back to the default locale if the requested locale is not found.
71
+ */
72
+ declare function loadTranslations<T extends Messages = Messages>(locale: string): Promise<T>;
73
+ /**
74
+ * Creates a message loader function bound to a specific messages path.
75
+ * Use this if you need custom message loading logic.
76
+ */
77
+ declare function createMessageLoader<T extends Messages = Messages>(messagesPath: string, defaultLocale: string, locales: string[]): (locale: string) => Promise<T>;
78
+
79
+ /**
80
+ * Helper function to define and initialize i18n configuration.
81
+ * Call this in your i18n.config.ts file.
82
+ *
83
+ * @example
84
+ * // i18n.config.ts
85
+ * import { defineI18nConfig } from 'i18n-llm';
86
+ *
87
+ * export default defineI18nConfig({
88
+ * defaultLocale: 'en',
89
+ * locales: ['en', 'ko', 'ja'] as const,
90
+ * messagesPath: './messages',
91
+ * });
92
+ */
93
+ declare function defineI18nConfig<T extends readonly string[]>(config: {
94
+ defaultLocale: T[number];
95
+ locales: T;
96
+ messagesPath: string;
97
+ }): I18nConfig;
98
+
99
+ export { I18nConfig, InterpolationVariables, Messages, NestedKeyOf, NestedValueOf, TranslationsProvider, createMessageLoader, defineI18nConfig, getI18nConfig, getPreferredLanguage, getTranslations, loadTranslations, setI18nConfig };
package/dist/index.js ADDED
@@ -0,0 +1,122 @@
1
+ import {
2
+ createMessageLoader,
3
+ getI18nConfig,
4
+ loadTranslations,
5
+ setI18nConfig
6
+ } from "./chunk-OTLHL53Z.js";
7
+
8
+ // src/shared/interpolate.ts
9
+ function interpolate(template, variables) {
10
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
11
+ if (Object.prototype.hasOwnProperty.call(variables, key)) {
12
+ return String(variables[key]);
13
+ }
14
+ return match;
15
+ });
16
+ }
17
+
18
+ // src/shared/nested-key-utils.ts
19
+ function getNestedValue(obj, path) {
20
+ const keys = path.split(".");
21
+ let value = obj;
22
+ for (const key of keys) {
23
+ if (value === null || value === void 0) return void 0;
24
+ value = value[key];
25
+ }
26
+ return value;
27
+ }
28
+
29
+ // src/server/get-translations.ts
30
+ async function getTranslations(namespace, locale) {
31
+ const messages = await loadTranslations(locale);
32
+ function t(key, variables) {
33
+ const namespaceMessages = messages[namespace];
34
+ const value = getNestedValue(namespaceMessages, key);
35
+ if (value === void 0 || value === null) {
36
+ console.warn(
37
+ `i18n-llm: Translation for key "${String(namespace)}.${key}" not found.`
38
+ );
39
+ return key;
40
+ }
41
+ if (typeof value === "string" && variables) {
42
+ return interpolate(value, variables);
43
+ }
44
+ return value;
45
+ }
46
+ return t;
47
+ }
48
+
49
+ // src/client/translations-context.tsx
50
+ import { createContext } from "react";
51
+ import { jsx } from "react/jsx-runtime";
52
+ var TranslationsContext = createContext({
53
+ messages: {},
54
+ locale: ""
55
+ });
56
+ function ClientTranslationsProvider({
57
+ messages,
58
+ locale,
59
+ children
60
+ }) {
61
+ return /* @__PURE__ */ jsx(TranslationsContext.Provider, { value: { messages, locale }, children });
62
+ }
63
+
64
+ // src/server/translations-provider.tsx
65
+ import { jsx as jsx2 } from "react/jsx-runtime";
66
+ async function TranslationsProvider({ children, locale }) {
67
+ const messages = await loadTranslations(locale);
68
+ return /* @__PURE__ */ jsx2(ClientTranslationsProvider, { messages, locale, children });
69
+ }
70
+
71
+ // src/server/get-preferred-language.ts
72
+ var LANGUAGE_COOKIE_NAME = "preferred-language";
73
+ function getPreferredLanguage(request) {
74
+ const config = getI18nConfig();
75
+ if (!config) {
76
+ console.warn(
77
+ "i18n-llm: Configuration not set. Returning 'en' as default."
78
+ );
79
+ return "en";
80
+ }
81
+ const { defaultLocale, locales } = config;
82
+ const cookieValue = request.cookies.get(LANGUAGE_COOKIE_NAME)?.value;
83
+ if (cookieValue && locales.includes(cookieValue)) {
84
+ return cookieValue;
85
+ }
86
+ const acceptLanguage = request.headers.get("accept-language");
87
+ if (acceptLanguage) {
88
+ const langs = acceptLanguage.split(",").map((lang) => lang.split(";")[0]);
89
+ for (const lang of langs) {
90
+ const shortLang = lang.slice(0, 2).toLowerCase();
91
+ if (locales.includes(shortLang)) {
92
+ return shortLang;
93
+ }
94
+ }
95
+ }
96
+ return defaultLocale;
97
+ }
98
+
99
+ // src/index.ts
100
+ function defineI18nConfig(config) {
101
+ const i18nConfig = {
102
+ defaultLocale: config.defaultLocale,
103
+ locales: config.locales,
104
+ messagesPath: config.messagesPath
105
+ };
106
+ setI18nConfig({
107
+ defaultLocale: config.defaultLocale,
108
+ locales: [...config.locales],
109
+ messagesPath: config.messagesPath
110
+ });
111
+ return i18nConfig;
112
+ }
113
+ export {
114
+ TranslationsProvider,
115
+ createMessageLoader,
116
+ defineI18nConfig,
117
+ getI18nConfig,
118
+ getPreferredLanguage,
119
+ getTranslations,
120
+ loadTranslations,
121
+ setI18nConfig
122
+ };
@@ -0,0 +1,31 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { c as MiddlewareConfig } from './index-KVK-m3p1.js';
3
+ import 'react';
4
+
5
+ /**
6
+ * Defines the i18n middleware configuration.
7
+ * Call this once in your middleware.ts file.
8
+ *
9
+ * @example
10
+ * // middleware.ts
11
+ * import { defineMiddleware, middleware } from 'i18n-llm/middleware';
12
+ *
13
+ * defineMiddleware({
14
+ * locales: ['en', 'ko', 'ja'],
15
+ * defaultLocale: 'en',
16
+ * });
17
+ *
18
+ * export { middleware };
19
+ *
20
+ * export const config = {
21
+ * matcher: ['/((?!api|_next|.*\\..*).*)'],
22
+ * };
23
+ */
24
+ declare function defineMiddleware(config: MiddlewareConfig): void;
25
+ /**
26
+ * The i18n middleware function.
27
+ * Make sure to call defineMiddleware() before exporting this.
28
+ */
29
+ declare function middleware(request: NextRequest): NextResponse;
30
+
31
+ export { defineMiddleware, middleware };
@@ -0,0 +1,75 @@
1
+ // src/middleware.ts
2
+ import { NextResponse } from "next/server";
3
+ var LANGUAGE_COOKIE_NAME = "preferred-language";
4
+ var middlewareConfig = null;
5
+ function defineMiddleware(config) {
6
+ middlewareConfig = config;
7
+ }
8
+ function middleware(request) {
9
+ if (!middlewareConfig) {
10
+ console.error(
11
+ "i18n-llm: Middleware not configured. Call defineMiddleware() first."
12
+ );
13
+ return NextResponse.next();
14
+ }
15
+ const {
16
+ locales,
17
+ defaultLocale,
18
+ localePrefix = "always",
19
+ redirects = []
20
+ } = middlewareConfig;
21
+ const { pathname } = request.nextUrl;
22
+ if (pathname.startsWith("/_next") || pathname.startsWith("/api") || pathname.includes(".")) {
23
+ return NextResponse.next();
24
+ }
25
+ const pathnameLocale = locales.find(
26
+ (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
27
+ );
28
+ const preferredLocale = getPreferredLocale(request, locales, defaultLocale);
29
+ if (pathname === "/") {
30
+ if (localePrefix === "always") {
31
+ return NextResponse.redirect(
32
+ new URL(`/${preferredLocale}`, request.url)
33
+ );
34
+ }
35
+ return NextResponse.next();
36
+ }
37
+ if (!pathnameLocale) {
38
+ if (localePrefix === "always") {
39
+ return NextResponse.redirect(
40
+ new URL(`/${preferredLocale}${pathname}`, request.url)
41
+ );
42
+ }
43
+ return NextResponse.next();
44
+ }
45
+ for (const redirect of redirects) {
46
+ const pathWithoutLocale = pathname.replace(`/${pathnameLocale}`, "");
47
+ if (pathWithoutLocale === redirect.from) {
48
+ return NextResponse.redirect(
49
+ new URL(`/${pathnameLocale}${redirect.to}`, request.url)
50
+ );
51
+ }
52
+ }
53
+ return NextResponse.next();
54
+ }
55
+ function getPreferredLocale(request, locales, defaultLocale) {
56
+ const cookieValue = request.cookies.get(LANGUAGE_COOKIE_NAME)?.value;
57
+ if (cookieValue && locales.includes(cookieValue)) {
58
+ return cookieValue;
59
+ }
60
+ const acceptLanguage = request.headers.get("accept-language");
61
+ if (acceptLanguage) {
62
+ const langs = acceptLanguage.split(",").map((lang) => lang.split(";")[0]);
63
+ for (const lang of langs) {
64
+ const shortLang = lang.slice(0, 2).toLowerCase();
65
+ if (locales.includes(shortLang)) {
66
+ return shortLang;
67
+ }
68
+ }
69
+ }
70
+ return defaultLocale;
71
+ }
72
+ export {
73
+ defineMiddleware,
74
+ middleware
75
+ };
package/llm.txt ADDED
@@ -0,0 +1,254 @@
1
+ # i18n-llm Setup Guide
2
+
3
+ You are setting up i18n-llm for a Next.js App Router project.
4
+ Follow these steps exactly in order.
5
+
6
+ ## Step 1: Install the package
7
+
8
+ Run this command:
9
+ ```bash
10
+ npm install @meursyphus/i18n-llm
11
+ ```
12
+
13
+ ## Step 2: Create i18n.config.ts in project root
14
+
15
+ Create file `i18n.config.ts`:
16
+ ```typescript
17
+ import { defineI18nConfig } from '@meursyphus/i18n-llm';
18
+
19
+ export default defineI18nConfig({
20
+ defaultLocale: 'en',
21
+ locales: ['en', 'ko'] as const,
22
+ messagesPath: './messages',
23
+ });
24
+
25
+ export type Locale = 'en' | 'ko';
26
+ ```
27
+
28
+ ## Step 3: Create middleware.ts in project root
29
+
30
+ Create file `middleware.ts`:
31
+ ```typescript
32
+ import { defineMiddleware, middleware } from '@meursyphus/i18n-llm/middleware';
33
+
34
+ defineMiddleware({
35
+ locales: ['en', 'ko'],
36
+ defaultLocale: 'en',
37
+ });
38
+
39
+ export { middleware };
40
+
41
+ export const config = {
42
+ matcher: ['/((?!api|_next|.*\\..*).*)'],
43
+ };
44
+ ```
45
+
46
+ ## Step 4: Create messages directory structure
47
+
48
+ Create these files:
49
+
50
+ `messages/types.ts`:
51
+ ```typescript
52
+ export interface Messages {
53
+ common: {
54
+ title: string;
55
+ greeting: string; // "Hello, {name}!"
56
+ };
57
+ }
58
+ ```
59
+
60
+ `messages/en/index.ts`:
61
+ ```typescript
62
+ import type { Messages } from '../types';
63
+
64
+ const messages: Messages = {
65
+ common: {
66
+ title: 'Welcome',
67
+ greeting: 'Hello, {name}!',
68
+ },
69
+ };
70
+
71
+ export default messages;
72
+ ```
73
+
74
+ `messages/ko/index.ts`:
75
+ ```typescript
76
+ import type { Messages } from '../types';
77
+
78
+ const messages: Messages = {
79
+ common: {
80
+ title: '환영합니다',
81
+ greeting: '안녕하세요, {name}님!',
82
+ },
83
+ };
84
+
85
+ export default messages;
86
+ ```
87
+
88
+ ## Step 5: Update app directory structure
89
+
90
+ Rename `app/` contents to `app/[lang]/`:
91
+
92
+ Before:
93
+ ```
94
+ app/
95
+ ├── layout.tsx
96
+ ├── page.tsx
97
+ └── about/page.tsx
98
+ ```
99
+
100
+ After:
101
+ ```
102
+ app/
103
+ └── [lang]/
104
+ ├── layout.tsx
105
+ ├── page.tsx
106
+ └── about/page.tsx
107
+ ```
108
+
109
+ ## Step 6: Update layout.tsx
110
+
111
+ Replace `app/[lang]/layout.tsx`:
112
+ ```typescript
113
+ import { TranslationsProvider } from '@meursyphus/i18n-llm';
114
+
115
+ interface LayoutProps {
116
+ children: React.ReactNode;
117
+ params: Promise<{ lang: string }>;
118
+ }
119
+
120
+ export default async function RootLayout({ children, params }: LayoutProps) {
121
+ const { lang } = await params;
122
+
123
+ return (
124
+ <html lang={lang}>
125
+ <body>
126
+ <TranslationsProvider locale={lang}>
127
+ {children}
128
+ </TranslationsProvider>
129
+ </body>
130
+ </html>
131
+ );
132
+ }
133
+ ```
134
+
135
+ ## Step 7: Use translations in components
136
+
137
+ Server Component:
138
+ ```typescript
139
+ import { getTranslations } from '@meursyphus/i18n-llm';
140
+
141
+ export default async function Page({ params }: { params: Promise<{ lang: string }> }) {
142
+ const { lang } = await params;
143
+ const t = await getTranslations('common', lang);
144
+
145
+ return (
146
+ <div>
147
+ <h1>{t('title')}</h1>
148
+ <p>{t('greeting', { name: 'World' })}</p>
149
+ </div>
150
+ );
151
+ }
152
+ ```
153
+
154
+ Client Component:
155
+ ```typescript
156
+ 'use client';
157
+
158
+ import { useTranslations } from '@meursyphus/i18n-llm/client';
159
+
160
+ export function Greeting({ name }: { name: string }) {
161
+ const t = useTranslations('common');
162
+
163
+ return <p>{t('greeting', { name })}</p>;
164
+ }
165
+ ```
166
+
167
+ ## Step 8: Language Switcher (Optional)
168
+
169
+ Create a language switcher component:
170
+ ```typescript
171
+ 'use client';
172
+
173
+ import { useCurrentLanguage } from '@meursyphus/i18n-llm/client';
174
+ import { setLanguagePreference } from '@meursyphus/i18n-llm/actions';
175
+ import { usePathname } from 'next/navigation';
176
+
177
+ export function LanguageSwitcher() {
178
+ const currentLang = useCurrentLanguage();
179
+ const pathname = usePathname();
180
+
181
+ const handleChange = async (locale: string) => {
182
+ await setLanguagePreference(locale, pathname);
183
+ };
184
+
185
+ return (
186
+ <select value={currentLang} onChange={(e) => handleChange(e.target.value)}>
187
+ <option value="en">English</option>
188
+ <option value="ko">한국어</option>
189
+ </select>
190
+ );
191
+ }
192
+ ```
193
+
194
+ ## Step 9: Generate Metadata (Optional)
195
+
196
+ Use translations in generateMetadata:
197
+ ```typescript
198
+ import { getTranslations } from '@meursyphus/i18n-llm';
199
+ import type { Metadata } from 'next';
200
+
201
+ interface PageProps {
202
+ params: Promise<{ lang: string }>;
203
+ }
204
+
205
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
206
+ const { lang } = await params;
207
+ const t = await getTranslations('common', lang);
208
+
209
+ return {
210
+ title: t('title'),
211
+ description: t('description'),
212
+ };
213
+ }
214
+ ```
215
+
216
+ ## Setup Complete!
217
+
218
+ i18n-llm has been configured successfully.
219
+ The app now supports English (en) and Korean (ko).
220
+
221
+ To add more locales:
222
+ 1. Add locale to `locales` array in `i18n.config.ts`
223
+ 2. Add locale to `locales` array in `middleware.ts` (in defineMiddleware)
224
+ 3. Create new message file at `messages/{locale}/index.ts`
225
+
226
+ ## Variable Interpolation
227
+
228
+ Use `{variableName}` in translations:
229
+
230
+ ```typescript
231
+ // In messages file
232
+ "greeting": "Hello, {name}! You have {count} new messages."
233
+
234
+ // In component
235
+ t('greeting', { name: 'John', count: 5 })
236
+ // Output: "Hello, John! You have 5 new messages."
237
+ ```
238
+
239
+ ## Nested Keys
240
+
241
+ Access nested translations with dot notation:
242
+
243
+ ```typescript
244
+ // In messages file
245
+ {
246
+ nav: {
247
+ home: 'Home',
248
+ about: 'About',
249
+ }
250
+ }
251
+
252
+ // In component
253
+ t('nav.home') // "Home"
254
+ ```