generator-kodly-react-app 1.0.13 → 1.0.15

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.
@@ -1,4 +1,4 @@
1
1
  BASE_URL=/
2
2
  PORT=3000
3
3
  ALLOWED_HOSTS=localhost
4
- VITE_API_BASE_URL=https://localhost:8081
4
+ VITE_API_BASE_URL=http://localhost:8081
@@ -1,4 +1,4 @@
1
1
  BASE_URL=/
2
2
  PORT=3000
3
- ALLOWED_HOSTS=sandbox.beta.kodly.dev
4
- VITE_API_BASE_URL=${VITE_API_BASE_URL} //injected by env variables - https://sandbox.beta.kodly.dev/proxy/95-2
3
+ ALLOWED_HOSTS=sandbox.app.kodly.dev
4
+ VITE_API_BASE_URL=${VITE_API_BASE_URL} //injected by env variables - https://{port}-{projectSlug}-{userSlug}.sandbox.app.kodly.dev
@@ -0,0 +1,133 @@
1
+ # i18n Migration Guide
2
+
3
+ This document describes the i18n configuration changes that enable **both** flat and namespaced translation usage. Users with the older single-namespace setup can migrate using the instructions below.
4
+
5
+ ## Summary of Changes
6
+
7
+ The i18n setup now supports two usage patterns interchangeably:
8
+
9
+ | Pattern | Example | Use case |
10
+ |---------|---------|----------|
11
+ | **Flat (no namespace)** | `useTranslation()` + `t('namespace.section.key')` | Quick access, explicit full path |
12
+ | **Namespaced** | `useTranslation('namespace')` + `t('section.key')` | Modular components, shorter keys |
13
+
14
+ Previously, only the flat pattern worked because all translations lived under a single `translation` namespace. The namespaced pattern failed because i18next couldn't find the individual namespaces (the top-level keys in your merged translation object).
15
+
16
+ ---
17
+
18
+ ## Patch: `src/lib/i18n.ts`
19
+
20
+ ### Before (old configuration)
21
+
22
+ ```typescript
23
+ if (!i18n.isInitialized) {
24
+ const lng = getStoredLanguage();
25
+ i18n.use(initReactI18next).init({
26
+ resources: {
27
+ en: { translation: mergeTranslations('en') },
28
+ ja: { translation: mergeTranslations('ja') },
29
+ },
30
+ supportedLngs: supportedLanguages,
31
+ lng,
32
+ fallbackLng: defaultLanguage,
33
+ interpolation: {
34
+ escapeValue: false,
35
+ },
36
+ });
37
+ setLanguageHooks(lng);
38
+ }
39
+ ```
40
+
41
+ ### After (new configuration)
42
+
43
+ ```typescript
44
+ if (!i18n.isInitialized) {
45
+ const lng = getStoredLanguage();
46
+ const enResources = mergeTranslations('en');
47
+ const jaResources = mergeTranslations('ja');
48
+ i18n.use(initReactI18next).init({
49
+ resources: {
50
+ en: {
51
+ translation: enResources,
52
+ ...enResources,
53
+ },
54
+ ja: {
55
+ translation: jaResources,
56
+ ...jaResources,
57
+ },
58
+ },
59
+ defaultNS: 'translation',
60
+ ns: ['translation', ...Object.keys(enResources)],
61
+ supportedLngs: supportedLanguages,
62
+ lng,
63
+ fallbackLng: defaultLanguage,
64
+ interpolation: {
65
+ escapeValue: false,
66
+ },
67
+ });
68
+ setLanguageHooks(lng);
69
+ }
70
+ ```
71
+
72
+ ### What changed
73
+
74
+ 1. **Resources structure**: Each language now exposes both:
75
+ - `translation` – the full merged object for flat keys, e.g. `t('namespace.section.key')`
76
+ - Individual namespaces – one per top-level key in the merged object, for `useTranslation('namespace')` + `t('section.key')`
77
+
78
+ 2. **`defaultNS: 'translation'`**: Ensures `useTranslation()` without arguments uses the flat namespace.
79
+
80
+ 3. **`ns` array**: Must list `'translation'` plus every top-level key returned by `mergeTranslations()` (e.g. `['translation', ...Object.keys(enResources)]`) so i18next can load and resolve them correctly.
81
+
82
+ ---
83
+
84
+ ## Migration Steps
85
+
86
+ ### 1. Update `src/lib/i18n.ts`
87
+
88
+ Replace the `if (!i18n.isInitialized)` block with the new configuration above. The rest of the file (imports, `getLanguage`, `changeLanguage`, etc.) stays the same.
89
+
90
+ ### 2. Verify translation key paths (flat usage)
91
+
92
+ With the default `translation` namespace, keys must include the full path from the root of your merged translations. The format is the namespace (top-level key) followed by the path inside it:
93
+
94
+ - **Format**: `t('<namespace>.<path.to.key>')`
95
+ - **Example**: If your merged object has a top-level key `auth` with `login.title` inside it, use `t('auth.login.title')`.
96
+
97
+ If a key used to work as `t('someKey')` and now fails, it likely lives under a namespace in your structure—update it to `t('<namespace>.<someKey>')` (or the full path that matches your locale JSON).
98
+
99
+ ### 3. Optional: Use namespaced pattern in components
100
+
101
+ You can migrate components to the namespaced pattern for shorter keys:
102
+
103
+ ```tsx
104
+ // Before (flat)
105
+ const { t } = useTranslation();
106
+ return <h1>{t('namespace.section.title')}</h1>;
107
+
108
+ // After (namespaced) – optional
109
+ const { t } = useTranslation('namespace');
110
+ return <h1>{t('section.title')}</h1>;
111
+ ```
112
+
113
+ Both forms work. Choose what fits your component structure.
114
+
115
+ ---
116
+
117
+ ## Available Namespaces
118
+
119
+ Namespaces in your project are determined by **the top-level keys of the object returned by `mergeTranslations(lng)`**.
120
+
121
+ - **`translation`** – Always present. Contains the full merged tree so flat keys like `t('namespace.section.key')` work.
122
+ - **Other namespaces** – One for each top-level key in that merged object (e.g. if your merge yields `{ auth, app, common }`, you get namespaces `auth`, `app`, and `common`). Their contents match whatever structure your locale files and `mergeTranslations` produce.
123
+
124
+ To see which namespaces you have, inspect the keys of the result of `mergeTranslations('en')` (or your default language) and use that same list in the `ns` option in `i18n.ts`.
125
+
126
+ ---
127
+
128
+ ## No Changes Required
129
+
130
+ - **`src/locales/index.ts`** – `mergeTranslations` is unchanged
131
+ - **Locale JSON files** – No structure changes
132
+ - **Components using flat keys** – Existing `t('namespace.path.to.key')` calls continue to work
133
+ - **`LanguageSwitcher`** – Uses `i18n` only; no translation keys
@@ -3,7 +3,7 @@
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "version": "0.0.1",
6
- "packageManager": "pnpm@10.28.0",
6
+ "packageManager": "pnpm@10.28.2",
7
7
  "scripts": {
8
8
  "generate-routes": "tsr generate",
9
9
  "watch-routes": "tsr watch",
@@ -19,40 +19,40 @@
19
19
  "openapi-ts:sandbox": "MODE=sandbox openapi-ts"
20
20
  },
21
21
  "dependencies": {
22
- "@hey-api/openapi-ts": "^0.90.2",
22
+ "@hey-api/openapi-ts": "^0.92.3",
23
23
  "@hookform/resolvers": "^5.2.2",
24
24
  "@radix-ui/react-label": "^2.1.8",
25
25
  "@radix-ui/react-select": "^2.2.6",
26
26
  "@radix-ui/react-separator": "^1.1.8",
27
27
  "@radix-ui/react-slot": "^1.2.4",
28
- "@tanstack/react-query": "^5.90.16",
29
- "@tanstack/react-router": "^1.145.7",
30
- "@tanstack/router-plugin": "^1.145.7",
31
- "axios": "^1.13.2",
28
+ "@tanstack/react-query": "^5.90.20",
29
+ "@tanstack/react-router": "^1.158.1",
30
+ "@tanstack/router-plugin": "^1.158.1",
31
+ "axios": "^1.13.4",
32
32
  "class-variance-authority": "^0.7.1",
33
33
  "clsx": "^2.1.1",
34
- "dotenv": "^17.2.3",
35
- "i18next": "^25.7.3",
36
- "jotai": "^2.16.1",
37
- "lucide-react": "^0.562.0",
38
- "react": "^19.2.3",
39
- "react-dom": "^19.2.3",
40
- "react-hook-form": "^7.70.0",
41
- "react-i18next": "^16.5.1",
34
+ "dotenv": "^17.2.4",
35
+ "i18next": "^25.8.4",
36
+ "jotai": "^2.17.1",
37
+ "lucide-react": "^0.563.0",
38
+ "react": "^19.2.4",
39
+ "react-dom": "^19.2.4",
40
+ "react-hook-form": "^7.71.1",
41
+ "react-i18next": "^16.5.4",
42
42
  "sonner": "^2.0.7",
43
43
  "tailwind-merge": "^3.4.0",
44
44
  "tailwindcss-animate": "^1.0.7",
45
45
  "tw-animate-css": "^1.4.0",
46
- "zod": "^4.3.5"
46
+ "zod": "^4.3.6"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@tailwindcss/postcss": "^4.1.18",
50
50
  "@tailwindcss/vite": "^4.1.18",
51
- "@tanstack/router-cli": "^1.145.7",
52
- "@types/node": "^25.0.3",
53
- "@types/react": "^19.2.7",
51
+ "@tanstack/router-cli": "^1.158.1",
52
+ "@types/node": "^25.2.1",
53
+ "@types/react": "^19.2.13",
54
54
  "@types/react-dom": "^19.2.3",
55
- "@vitejs/plugin-react": "^5.1.2",
55
+ "@vitejs/plugin-react": "^5.1.3",
56
56
  "babel-plugin-react-compiler": "^1.0.0",
57
57
  "npm-run-all": "^4.1.5",
58
58
  "tailwindcss": "^4.1.18",
@@ -29,11 +29,21 @@ const getStoredLanguage = (): SupportedLanguages => {
29
29
 
30
30
  if (!i18n.isInitialized) {
31
31
  const lng = getStoredLanguage();
32
+ const enResources = mergeTranslations('en');
33
+ const jaResources = mergeTranslations('ja');
32
34
  i18n.use(initReactI18next).init({
33
35
  resources: {
34
- en: { translation: mergeTranslations('en') },
35
- ja: { translation: mergeTranslations('ja') },
36
+ en: {
37
+ translation: enResources,
38
+ ...enResources,
39
+ },
40
+ ja: {
41
+ translation: jaResources,
42
+ ...jaResources,
43
+ },
36
44
  },
45
+ defaultNS: 'translation',
46
+ ns: ['translation', 'auth', 'app', 'landing', 'common', 'theme'],
37
47
  supportedLngs: supportedLanguages,
38
48
  lng,
39
49
  fallbackLng: defaultLanguage,
@@ -5,18 +5,18 @@ import { Route as LoginRoute } from '@/routes/auth/login';
5
5
  import { Route as SignupRoute } from '@/routes/auth/signup';
6
6
 
7
7
  export function AuthHero() {
8
- const { t } = useTranslation();
8
+ const { t } = useTranslation('landing');
9
9
 
10
10
  return (
11
11
  <>
12
- <h1 className='text-4xl md:text-5xl lg:text-6xl font-bold mb-6'>{t('landing.hero.title')}</h1>
13
- <p className='text-xl md:text-2xl text-muted-foreground mb-12 max-w-2xl mx-auto'>{t('landing.hero.description')}</p>
12
+ <h1 className='text-4xl md:text-5xl lg:text-6xl font-bold mb-6'>{t('hero.title')}</h1>
13
+ <p className='text-xl md:text-2xl text-muted-foreground mb-12 max-w-2xl mx-auto'>{t('hero.description')}</p>
14
14
  <div className='flex flex-col sm:flex-row gap-4 justify-center items-center'>
15
15
  <Link to={LoginRoute.to}>
16
16
  <Button
17
17
  size='lg'
18
18
  className='w-full sm:w-auto min-w-[150px]'>
19
- {t('landing.hero.loginButton')}
19
+ {t('hero.loginButton')}
20
20
  </Button>
21
21
  </Link>
22
22
  <Link to={SignupRoute.to}>
@@ -24,7 +24,7 @@ export function AuthHero() {
24
24
  size='lg'
25
25
  variant='outline'
26
26
  className='w-full sm:w-auto min-w-[150px]'>
27
- {t('landing.hero.signupButton')}
27
+ {t('hero.signupButton')}
28
28
  </Button>
29
29
  </Link>
30
30
  </div>
@@ -11,7 +11,7 @@ function NotFound() {
11
11
  <div className="flex h-screen w-screen items-center justify-center">
12
12
  <div className="text-center">
13
13
  <h1 className="text-4xl font-bold mb-4">404</h1>
14
- <p className="text-muted-foreground">{t("notFound.message")}</p>
14
+ <p className="text-muted-foreground">{t("common.notFound.message")}</p>
15
15
  </div>
16
16
  </div>
17
17
  );
@@ -32,6 +32,7 @@ export default defineConfig(({ mode }) => {
32
32
  },
33
33
  },
34
34
  server: {
35
+ host: true,
35
36
  port: parseInt(env.PORT || '3000', 10),
36
37
  allowedHosts: [env.ALLOWED_HOSTS || 'localhost'],
37
38
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generator-kodly-react-app",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "A Yeoman generator for creating React.js applications with Vite, TanStack Router, authentication, and i18n",
5
5
  "main": "index.js",
6
6
  "type": "module",