@akanjs/cli 0.9.60-canary.8 → 1.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/cjs/index.js +16 -47
- package/cjs/src/guidelines/scalarConstant/scalarConstant.generate.json +24 -20
- package/cjs/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
- package/cjs/src/guidelines/scalarDictionary/scalarDictionary.generate.json +32 -32
- package/cjs/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
- package/cjs/src/templates/app/app/[lang]/page.js +19 -36
- package/cjs/src/templates/workspaceRoot/package.json.template +5 -5
- package/esm/index.js +16 -47
- package/esm/src/guidelines/scalarConstant/scalarConstant.generate.json +24 -20
- package/esm/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
- package/esm/src/guidelines/scalarDictionary/scalarDictionary.generate.json +32 -32
- package/esm/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
- package/esm/src/templates/app/app/[lang]/page.js +19 -36
- package/esm/src/templates/workspaceRoot/package.json.template +5 -5
- package/package.json +5 -4
- package/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
- package/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
- package/src/library/library.command.d.ts +0 -2
- package/src/library/library.runner.d.ts +0 -2
- package/src/library/library.script.d.ts +0 -2
- package/src/scalar/scalar.command.d.ts +1 -1
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.generate.json +0 -135
- package/cjs/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.generate.json +0 -135
- package/esm/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
- package/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
"title": "Scalar Dictionary",
|
|
3
3
|
"description": "How to create and maintain scalar.dictionary.ts files in Akan.js",
|
|
4
4
|
"scans": [
|
|
5
|
+
{
|
|
6
|
+
"type": "source",
|
|
7
|
+
"description": "scalarDictionary builder function implementation",
|
|
8
|
+
"path": "pkgs/@akanjs/dictionary/src/scalarDictionary.ts"
|
|
9
|
+
},
|
|
5
10
|
{
|
|
6
11
|
"type": "source",
|
|
7
12
|
"description": "Translation types and interfaces",
|
|
@@ -9,7 +14,7 @@
|
|
|
9
14
|
},
|
|
10
15
|
{
|
|
11
16
|
"type": "source",
|
|
12
|
-
"description": "
|
|
17
|
+
"description": "Dictionary exports and types",
|
|
13
18
|
"path": "pkgs/@akanjs/dictionary/src/index.ts"
|
|
14
19
|
},
|
|
15
20
|
{
|
|
@@ -23,16 +28,9 @@
|
|
|
23
28
|
"type": "example",
|
|
24
29
|
"description": "Scalar dictionaries with enum translations",
|
|
25
30
|
"path": "{apps,libs}/**/lib/__scalar/*/*.dictionary.ts",
|
|
26
|
-
"query": "
|
|
31
|
+
"query": "/\\.enum</",
|
|
27
32
|
"sample": 3
|
|
28
33
|
},
|
|
29
|
-
{
|
|
30
|
-
"type": "example",
|
|
31
|
-
"description": "Scalar dictionaries with section separators",
|
|
32
|
-
"path": "{apps,libs}/**/lib/__scalar/*/*.dictionary.ts",
|
|
33
|
-
"query": "/={10,}/",
|
|
34
|
-
"sample": 2
|
|
35
|
-
},
|
|
36
34
|
{
|
|
37
35
|
"type": "example",
|
|
38
36
|
"description": "Complete scalar dictionary with field and enum translations",
|
|
@@ -42,9 +40,9 @@
|
|
|
42
40
|
},
|
|
43
41
|
{
|
|
44
42
|
"type": "example",
|
|
45
|
-
"description": "
|
|
43
|
+
"description": "Scalar dictionaries with multiple enums",
|
|
46
44
|
"path": "{apps,libs}/**/lib/__scalar/*/*.dictionary.ts",
|
|
47
|
-
"query": "
|
|
45
|
+
"query": "/\\.enum<.*\\.enum</",
|
|
48
46
|
"sample": 2
|
|
49
47
|
}
|
|
50
48
|
],
|
|
@@ -52,31 +50,33 @@
|
|
|
52
50
|
"filePath": "./scalarDictionary.instruction.md",
|
|
53
51
|
"contents": [
|
|
54
52
|
"Purpose of scalar dictionary files",
|
|
55
|
-
"
|
|
56
|
-
"Required
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
53
|
+
"File structure and naming conventions",
|
|
54
|
+
"Required imports from @akanjs/dictionary",
|
|
55
|
+
"scalarDictionary() builder pattern",
|
|
56
|
+
"Builder methods: .of(), .model<T>(), .enum<T>()",
|
|
57
|
+
"Translation format with t() function and .desc() method",
|
|
58
|
+
"Type imports from constant file using import type",
|
|
59
|
+
"Enum name matching with enumOf() name",
|
|
60
|
+
"Type safety through generic parameters",
|
|
63
61
|
"Common mistakes and fixes",
|
|
64
|
-
"Best practices for
|
|
65
|
-
"
|
|
62
|
+
"Best practices for translations",
|
|
63
|
+
"Implementation checklist",
|
|
64
|
+
"Full examples with multiple enums"
|
|
66
65
|
],
|
|
67
66
|
"rules": [
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
67
|
+
"Import scalarDictionary from '@akanjs/dictionary'",
|
|
68
|
+
"Import types from constant file using 'import type' syntax",
|
|
69
|
+
"Initialize with language order array: scalarDictionary(['en', 'ko'])",
|
|
70
|
+
"Use .of() method to define scalar name and description",
|
|
71
|
+
"Use .model<Type>() method with generic to define field translations",
|
|
72
|
+
"Use .enum<Type>('enumName', ...) method to define enum translations",
|
|
73
|
+
"The enum name string in .enum() must exactly match the first argument of enumOf() in constant file",
|
|
74
|
+
"Always chain .desc() after t() to provide descriptions: t([...]).desc([...])",
|
|
71
75
|
"Use exactly two string elements in translation arrays: [English, Korean]",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"Always use camelCase for field names, exactly matching the constant file",
|
|
77
|
-
"Follow the exact format enum-fieldName-enumValue for enum translations",
|
|
78
|
-
"Group translations by sections with comment separators for better readability",
|
|
79
|
-
"Use proper naming: myScalarDictionary for the exported constant (matching the model name)"
|
|
76
|
+
"Export as 'dictionary': export const dictionary = scalarDictionary(...)...",
|
|
77
|
+
"Use type generics (.model<Type>, .enum<Type>) for TypeScript validation",
|
|
78
|
+
"Provide meaningful descriptions that explain purpose, not just repeat labels",
|
|
79
|
+
"Match language order consistently across all dictionaries in the project"
|
|
80
80
|
]
|
|
81
81
|
},
|
|
82
82
|
"page": "/[lang]/akanjs/(docs)/docs/scalar/dictionary/page.tsx"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Scalar dictionary files in Akan.js provide internationalization (i18n) support by:
|
|
6
6
|
|
|
7
|
-
- Defining translations for
|
|
7
|
+
- Defining translations for scalar names, fields, and enum values
|
|
8
8
|
- Supporting multiple languages (primarily English and Korean)
|
|
9
9
|
- Enabling consistent terminology across the application
|
|
10
10
|
- Providing field descriptions for documentation and tooltips
|
|
@@ -18,330 +18,256 @@ Scalar dictionary files in Akan.js provide internationalization (i18n) support b
|
|
|
18
18
|
```
|
|
19
19
|
{app,lib}/
|
|
20
20
|
└── */lib/__scalar/
|
|
21
|
-
└── <scalarName>/
|
|
22
|
-
├── <scalarName>.constant.ts
|
|
23
|
-
└── <scalarName>.dictionary.ts
|
|
21
|
+
└── <scalarName>/ # camelCase directory
|
|
22
|
+
├── <scalarName>.constant.ts # scalar definition
|
|
23
|
+
└── <scalarName>.dictionary.ts # translations
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
###
|
|
26
|
+
### Key Benefits
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const modelDictionary = {
|
|
33
|
-
// Required model metadata
|
|
34
|
-
modelName: ["Model Name", "모델 이름"],
|
|
35
|
-
modelDesc: ["Model description", "모델 설명"],
|
|
36
|
-
|
|
37
|
-
// Field translations with descriptions
|
|
38
|
-
fieldName: ["Field Label", "필드 라벨"],
|
|
39
|
-
"desc-fieldName": ["Field description", "필드 설명"],
|
|
40
|
-
|
|
41
|
-
// Enum translations with descriptions
|
|
42
|
-
"enum-status-active": ["Active", "활성"],
|
|
43
|
-
"enumdesc-status-active": ["Active status", "활성 상태"],
|
|
44
|
-
} satisfies ModelDictionary<YourModel>;
|
|
45
|
-
|
|
46
|
-
export const yourModelDictionary = modelDictionary;
|
|
47
|
-
```
|
|
28
|
+
- End-to-end type safety
|
|
29
|
+
- Fluent builder pattern
|
|
30
|
+
- Automatic validation via generics
|
|
31
|
+
- Multi-language support
|
|
48
32
|
|
|
49
33
|
## Required Imports
|
|
50
34
|
|
|
51
35
|
```typescript
|
|
52
|
-
import {
|
|
53
|
-
import type { YourModel } from "./your-model.constant";
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Translation Patterns
|
|
36
|
+
import { scalarDictionary } from "@akanjs/dictionary";
|
|
57
37
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Every scalar dictionary must include these two translations at the beginning:
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
modelName: ["User", "사용자"], // Short display name
|
|
64
|
-
modelDesc: ["User account", "사용자 계정"], // Longer description
|
|
38
|
+
// Import types using "import type" for cleaner code
|
|
39
|
+
import type { YourScalar, YourEnum } from "./yourScalar.constant";
|
|
65
40
|
```
|
|
66
41
|
|
|
67
|
-
|
|
42
|
+
## Scalar Dictionary Builder
|
|
68
43
|
|
|
69
|
-
|
|
44
|
+
The `scalarDictionary()` function creates a type-safe dictionary using a fluent builder pattern.
|
|
70
45
|
|
|
71
|
-
|
|
72
|
-
// Field label
|
|
73
|
-
username: ["Username", "사용자명"],
|
|
46
|
+
### Builder Methods
|
|
74
47
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
48
|
+
| Method | Description | Example |
|
|
49
|
+
| ---------------------------- | -------------------------------- | -------------------------------------------------------------------------- |
|
|
50
|
+
| `.of((t) => ...)` | Define scalar name & description | `.of((t) => t(["Scalar Name", "스칼라 이름"]).desc(["Desc", "설명"]))` |
|
|
51
|
+
| `.model<T>((t) => ...)` | Define field translations | `.model<YourScalar>((t) => ({ fieldName: t(["Label", "레이블"]) }))` |
|
|
52
|
+
| `.enum<T>(name, (t) => ...)` | Define enum value translations | `.enum<YourEnum>("enumName", (t) => ({ value1: t(["Label", "레이블"]) }))` |
|
|
78
53
|
|
|
79
|
-
|
|
54
|
+
## Translation Format
|
|
80
55
|
|
|
81
|
-
Each
|
|
56
|
+
Each translation uses the `t()` function with an array of values. The array order matches the language order defined in `scalarDictionary()`.
|
|
82
57
|
|
|
83
58
|
```typescript
|
|
84
|
-
//
|
|
85
|
-
|
|
59
|
+
// Language order: ["en", "ko"]
|
|
60
|
+
// Index 0 = English, Index 1 = Korean
|
|
86
61
|
|
|
87
|
-
|
|
88
|
-
"enumdesc-status-active": ["Account is active", "계정이 활성화됨"],
|
|
62
|
+
t(["English Label", "한국어 레이블"]).desc(["English description", "한국어 설명"]);
|
|
89
63
|
```
|
|
90
64
|
|
|
91
|
-
###
|
|
92
|
-
|
|
93
|
-
Additional translations specific to this model but not tied to fields:
|
|
65
|
+
### Translation Methods
|
|
94
66
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
67
|
+
| Method | Purpose | Example |
|
|
68
|
+
| -------------- | ---------------------- | ---------------------------------------- |
|
|
69
|
+
| `t([...])` | Define the label/name | `t(["Status", "상태"])` |
|
|
70
|
+
| `.desc([...])` | Define the description | `.desc(["Current status", "현재 상태"])` |
|
|
98
71
|
|
|
99
|
-
|
|
72
|
+
> **Note**: Both label and description are required for complete internationalization support.
|
|
100
73
|
|
|
101
|
-
|
|
74
|
+
## Basic Structure
|
|
102
75
|
|
|
103
76
|
```typescript
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
userProfileTitle: ["User Profile", "사용자 프로필"],
|
|
116
|
-
// * ==================== Custom ==================== * //
|
|
77
|
+
import { scalarDictionary } from "@akanjs/dictionary";
|
|
78
|
+
import type { YourScalar, YourEnum } from "./yourScalar.constant";
|
|
79
|
+
|
|
80
|
+
export const dictionary = scalarDictionary(["en", "ko"])
|
|
81
|
+
.of((t) => t(["Scalar Name", "스칼라 이름"]).desc(["Scalar description", "스칼라 설명"]))
|
|
82
|
+
.model<YourScalar>((t) => ({
|
|
83
|
+
fieldName: t(["Field Label", "필드 레이블"]).desc(["Field description", "필드 설명"]),
|
|
84
|
+
}))
|
|
85
|
+
.enum<YourEnum>("enumName", (t) => ({
|
|
86
|
+
value1: t(["Value Label", "값 레이블"]).desc(["Value description", "값 설명"]),
|
|
87
|
+
}));
|
|
117
88
|
```
|
|
118
89
|
|
|
119
|
-
##
|
|
120
|
-
|
|
121
|
-
Always use the `satisfies` operator with `ModelDictionary<YourModel>` to ensure:
|
|
122
|
-
|
|
123
|
-
1. All required fields from the model are translated
|
|
124
|
-
2. Field names match exactly
|
|
125
|
-
3. Typescript will catch typos and missing fields
|
|
90
|
+
## Complete Example
|
|
126
91
|
|
|
127
92
|
```typescript
|
|
128
|
-
|
|
93
|
+
// File: libs/payment/lib/__scalar/price/price.dictionary.ts
|
|
94
|
+
import { scalarDictionary } from "@akanjs/dictionary";
|
|
95
|
+
import type { Currency, Price } from "./price.constant";
|
|
96
|
+
|
|
97
|
+
export const dictionary = scalarDictionary(["en", "ko"])
|
|
98
|
+
.of((t) => t(["Price", "가격"]).desc(["Price information", "가격 정보"]))
|
|
99
|
+
.model<Price>((t) => ({
|
|
100
|
+
amount: t(["Amount", "금액"]).desc(["Price amount", "가격 금액"]),
|
|
101
|
+
currency: t(["Currency", "통화"]).desc(["Currency type", "통화 유형"]),
|
|
102
|
+
}))
|
|
103
|
+
.enum<Currency>("currency", (t) => ({
|
|
104
|
+
usd: t(["USD", "달러"]).desc(["US Dollar", "미국 달러"]),
|
|
105
|
+
krw: t(["KRW", "원"]).desc(["Korean Won", "한국 원"]),
|
|
106
|
+
eur: t(["EUR", "유로"]).desc(["Euro", "유로"]),
|
|
107
|
+
}));
|
|
129
108
|
```
|
|
130
109
|
|
|
131
|
-
##
|
|
132
|
-
|
|
133
|
-
### Required Elements
|
|
134
|
-
|
|
135
|
-
- [ ] Import `ModelDictionary` from `@akanjs/dictionary`
|
|
136
|
-
- [ ] Import the model type from the constant file
|
|
137
|
-
- [ ] Include `modelName` and `modelDesc` translations
|
|
138
|
-
- [ ] Translate all fields from the model
|
|
139
|
-
- [ ] Add descriptions for all fields with `desc-` prefix
|
|
140
|
-
- [ ] Translate all enum values with `enum-field-value` pattern
|
|
141
|
-
- [ ] Add descriptions for all enum values with `enumdesc-` prefix
|
|
142
|
-
- [ ] Use `satisfies ModelDictionary<YourModel>` for type checking
|
|
143
|
-
- [ ] Export with the correct naming convention
|
|
144
|
-
|
|
145
|
-
### Translation Arrays
|
|
146
|
-
|
|
147
|
-
- [ ] Each translation is a tuple with exactly two elements
|
|
148
|
-
- [ ] First element is English translation
|
|
149
|
-
- [ ] Second element is Korean translation
|
|
150
|
-
- [ ] No trailing punctuation in translations
|
|
151
|
-
- [ ] First letter capitalized in English
|
|
152
|
-
|
|
153
|
-
### Organization
|
|
110
|
+
## Enum Name Matching
|
|
154
111
|
|
|
155
|
-
|
|
156
|
-
- [ ] Group enum values with their descriptions
|
|
157
|
-
- [ ] Use standard comment separators
|
|
158
|
-
- [ ] Place model metadata first, then fields, then enums
|
|
112
|
+
The first argument to `.enum()` must match the name used in `enumOf()` from the constant file. This ensures proper type checking and runtime resolution.
|
|
159
113
|
|
|
160
|
-
|
|
114
|
+
### Example
|
|
161
115
|
|
|
162
|
-
|
|
116
|
+
**constant.ts**:
|
|
163
117
|
|
|
164
118
|
```typescript
|
|
165
|
-
|
|
166
|
-
username: ["Username", "사용자명"],
|
|
167
|
-
|
|
168
|
-
// ✅ Correct
|
|
169
|
-
username: ["Username", "사용자명"],
|
|
170
|
-
"desc-username": ["User's login name", "로그인에 사용하는 이름"],
|
|
119
|
+
export class Currency extends enumOf("currency", ["usd", "krw", "eur"]) {}
|
|
171
120
|
```
|
|
172
121
|
|
|
173
|
-
|
|
122
|
+
**dictionary.ts**:
|
|
174
123
|
|
|
175
124
|
```typescript
|
|
176
|
-
//
|
|
177
|
-
"
|
|
178
|
-
|
|
179
|
-
"
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"enum-status-active": ["Active", "활성"],
|
|
183
|
-
"enumdesc-status-active": ["Account is active", "계정이 활성화됨"],
|
|
125
|
+
// Must match: "currency"
|
|
126
|
+
.enum<Currency>("currency", (t) => ({
|
|
127
|
+
usd: t(["USD", "달러"]).desc(["US Dollar", "미국 달러"]),
|
|
128
|
+
krw: t(["KRW", "원"]).desc(["Korean Won", "한국 원"]),
|
|
129
|
+
eur: t(["EUR", "유로"]).desc(["Euro", "유로"]),
|
|
130
|
+
}))
|
|
184
131
|
```
|
|
185
132
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
// ❌ Wrong (missing required metadata)
|
|
190
|
-
const modelDictionary = {
|
|
191
|
-
username: ["Username", "사용자명"],
|
|
192
|
-
// ...
|
|
133
|
+
> **Important**: The enum name string must exactly match the first argument of `enumOf()`. For example, `enumOf("journey", [...])` requires `.enum<Journey>("journey", ...)`.
|
|
193
134
|
|
|
194
|
-
|
|
195
|
-
const modelDictionary = {
|
|
196
|
-
modelName: ["User", "사용자"],
|
|
197
|
-
modelDesc: ["User account", "사용자 계정"],
|
|
198
|
-
username: ["Username", "사용자명"],
|
|
199
|
-
// ...
|
|
200
|
-
```
|
|
135
|
+
## Type Imports
|
|
201
136
|
|
|
202
|
-
|
|
137
|
+
Always import types from the constant file to ensure type safety. The generic parameters enforce that all fields and enum values have translations.
|
|
203
138
|
|
|
204
139
|
```typescript
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
140
|
+
import { scalarDictionary } from "@akanjs/dictionary";
|
|
141
|
+
|
|
142
|
+
// Import types using "import type" for cleaner code
|
|
143
|
+
import type { EncourageInfo, Inquiry, Journey } from "./encourageInfo.constant";
|
|
144
|
+
|
|
145
|
+
export const dictionary = scalarDictionary(["en", "ko"])
|
|
146
|
+
.of((t) => t(["Encourage Info", "격려 정보"]).desc(["Encouragement information", "격려 정보"]))
|
|
147
|
+
.model<EncourageInfo>((t) => ({
|
|
148
|
+
// TypeScript ensures all fields of EncourageInfo are defined
|
|
149
|
+
}))
|
|
150
|
+
.enum<Journey>("journey", (t) => ({
|
|
151
|
+
// TypeScript ensures all values of Journey enum are defined
|
|
152
|
+
}))
|
|
153
|
+
.enum<Inquiry>("inquiry", (t) => ({
|
|
154
|
+
// TypeScript ensures all values of Inquiry enum are defined
|
|
155
|
+
}));
|
|
212
156
|
```
|
|
213
157
|
|
|
214
|
-
###
|
|
158
|
+
### Type Safety Benefits
|
|
215
159
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
160
|
+
- **`import type`**: Use `import type` for type-only imports. This ensures no runtime code is included.
|
|
161
|
+
- **`.model<Type>`**: Provides autocomplete for field names and validates that all fields are translated.
|
|
162
|
+
- **`.enum<Type>`**: Provides autocomplete for enum values and validates that all values are translated.
|
|
219
163
|
|
|
220
|
-
|
|
221
|
-
const modelDictionary = {
|
|
222
|
-
// ...
|
|
223
|
-
} satisfies ModelDictionary<User>;
|
|
164
|
+
## Common Mistakes and Fixes
|
|
224
165
|
|
|
225
|
-
|
|
226
|
-
|
|
166
|
+
| Issue | Wrong ❌ | Correct ✅ |
|
|
167
|
+
| ----------------- | -------------------------------- | ----------------------------------------------- |
|
|
168
|
+
| Missing .desc() | `t(["Label", "레이블"])` | `t(["Label", "레이블"]).desc(["Desc", "설명"])` |
|
|
169
|
+
| Wrong enum name | `.enum<Journey>("Journey", ...)` | `.enum<Journey>("journey", ...)` |
|
|
170
|
+
| Missing export | `const dictionary = ...` | `export const dictionary = ...` |
|
|
171
|
+
| Wrong array order | `t(["한국어", "English"])` | `t(["English", "한국어"])` |
|
|
172
|
+
| Missing field | (TypeScript error) | All fields defined |
|
|
227
173
|
|
|
228
174
|
## Best Practices
|
|
229
175
|
|
|
230
|
-
### 1.
|
|
176
|
+
### 1. Always Use Type Generics
|
|
231
177
|
|
|
232
|
-
|
|
233
|
-
// First: model metadata
|
|
234
|
-
modelName: ["User", "사용자"],
|
|
235
|
-
modelDesc: ["User account", "사용자 계정"],
|
|
178
|
+
Use `.model<Type>` and `.enum<Type>` to ensure TypeScript validates all fields and values are translated.
|
|
236
179
|
|
|
237
|
-
|
|
238
|
-
username: ["Username", "사용자명"],
|
|
239
|
-
"desc-username": ["User's login name", "로그인에 사용하는 이름"],
|
|
180
|
+
### 2. Consistent Language Order
|
|
240
181
|
|
|
241
|
-
|
|
242
|
-
"enum-status-active": ["Active", "활성"],
|
|
243
|
-
"enumdesc-status-active": ["Account is active", "계정이 활성화됨"],
|
|
182
|
+
Always use the same language order (e.g., `["en", "ko"]`) across all dictionaries in your project.
|
|
244
183
|
|
|
245
|
-
|
|
246
|
-
profileHeading: ["Profile Details", "프로필 상세정보"],
|
|
247
|
-
```
|
|
184
|
+
### 3. Meaningful Descriptions
|
|
248
185
|
|
|
249
|
-
|
|
186
|
+
Provide helpful descriptions that explain the field's purpose, not just repeat the label.
|
|
250
187
|
|
|
251
188
|
```typescript
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
// * ==================== Model ==================== * //
|
|
255
|
-
|
|
256
|
-
// * ==================== Enums ==================== * //
|
|
257
|
-
// Enum translations go here
|
|
258
|
-
// * ==================== Enums ==================== * //
|
|
189
|
+
// ❌ Bad - description just repeats the label
|
|
190
|
+
amount: t(["Amount", "금액"]).desc(["Amount", "금액"]),
|
|
259
191
|
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
// * ==================== Custom ==================== * //
|
|
192
|
+
// ✅ Good - description explains the purpose
|
|
193
|
+
amount: t(["Amount", "금액"]).desc(["Price amount in selected currency", "선택된 통화의 가격 금액"]),
|
|
263
194
|
```
|
|
264
195
|
|
|
265
|
-
###
|
|
266
|
-
|
|
267
|
-
Follow the same field order as in the constant file for easier maintenance.
|
|
268
|
-
|
|
269
|
-
### 4. Naming Conventions
|
|
196
|
+
### 4. Export as 'dictionary'
|
|
270
197
|
|
|
271
|
-
|
|
272
|
-
- Field names must exactly match the constant file
|
|
273
|
-
- Use kebab-case for prefix separators: `desc-`, `enum-`, `enumdesc-`
|
|
274
|
-
|
|
275
|
-
### 5. Consistent Translation Style
|
|
276
|
-
|
|
277
|
-
- First letter capitalized for English
|
|
278
|
-
- No trailing punctuation
|
|
279
|
-
- Keep translations concise but clear
|
|
280
|
-
- Be consistent in terminology
|
|
281
|
-
|
|
282
|
-
## Full Example
|
|
198
|
+
Use the standard export name `dictionary` for consistency with the framework's auto-import system.
|
|
283
199
|
|
|
284
200
|
```typescript
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const modelDictionary = {
|
|
290
|
-
modelName: ["User", "사용자"],
|
|
291
|
-
modelDesc: ["System user account", "시스템 사용자 계정"],
|
|
292
|
-
|
|
293
|
-
// * ==================== Model ==================== * //
|
|
294
|
-
username: ["Username", "사용자명"],
|
|
295
|
-
"desc-username": ["Unique login identifier", "고유 로그인 식별자"],
|
|
296
|
-
|
|
297
|
-
email: ["Email", "이메일"],
|
|
298
|
-
"desc-email": ["Contact email address", "연락용 이메일 주소"],
|
|
201
|
+
// ✅ Correct
|
|
202
|
+
export const dictionary = scalarDictionary(["en", "ko"])...
|
|
203
|
+
```
|
|
299
204
|
|
|
300
|
-
|
|
301
|
-
"desc-displayName": ["Public name shown to others", "다른 사용자에게 표시되는 이름"],
|
|
205
|
+
## Implementation Checklist
|
|
302
206
|
|
|
303
|
-
|
|
304
|
-
"desc-status": ["Account status", "계정 상태"],
|
|
207
|
+
### Required Elements
|
|
305
208
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
209
|
+
- [ ] File location: `__scalar/<name>/<name>.dictionary.ts`
|
|
210
|
+
- [ ] Import `scalarDictionary` from `@akanjs/dictionary`
|
|
211
|
+
- [ ] Import types from constant file using `import type`
|
|
212
|
+
- [ ] Initialize with correct language order: `["en", "ko"]`
|
|
213
|
+
- [ ] Define scalar name/description with `.of()`
|
|
214
|
+
- [ ] Define all field translations with `.model<Type>()`
|
|
215
|
+
- [ ] Define all enum translations with `.enum<Type>(name)`
|
|
216
|
+
- [ ] Ensure enum name matches `enumOf()` name
|
|
217
|
+
- [ ] Include both label and description for all entries
|
|
218
|
+
- [ ] Export as `dictionary`
|
|
309
219
|
|
|
310
|
-
|
|
311
|
-
"enum-status-active": ["Active", "활성"],
|
|
312
|
-
"enumdesc-status-active": ["Account is enabled and usable", "계정이 활성화되어 사용 가능함"],
|
|
220
|
+
### Translation Format
|
|
313
221
|
|
|
314
|
-
|
|
315
|
-
|
|
222
|
+
- [ ] Each translation uses `t([...]).desc([...])`
|
|
223
|
+
- [ ] First element is English translation
|
|
224
|
+
- [ ] Second element is Korean translation
|
|
225
|
+
- [ ] No trailing punctuation in translations
|
|
226
|
+
- [ ] First letter capitalized in English
|
|
316
227
|
|
|
317
|
-
|
|
318
|
-
"enumdesc-status-banned": ["Account is permanently disabled", "계정이 영구적으로 비활성화됨"],
|
|
228
|
+
## Full Example with Multiple Enums
|
|
319
229
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
230
|
+
```typescript
|
|
231
|
+
// File: apps/example/lib/__scalar/encourageInfo/encourageInfo.dictionary.ts
|
|
232
|
+
import { scalarDictionary } from "@akanjs/dictionary";
|
|
233
|
+
import type { EncourageInfo, Inquiry, Journey } from "./encourageInfo.constant";
|
|
234
|
+
|
|
235
|
+
export const dictionary = scalarDictionary(["en", "ko"])
|
|
236
|
+
.of((t) => t(["Encourage Info", "격려 정보"]).desc(["User encouragement settings", "사용자 격려 설정"]))
|
|
237
|
+
.model<EncourageInfo>((t) => ({
|
|
238
|
+
journey: t(["Journey", "여정"]).desc(["User journey stage", "사용자 여정 단계"]),
|
|
239
|
+
inquiry: t(["Inquiry", "문의"]).desc(["Inquiry type", "문의 유형"]),
|
|
240
|
+
message: t(["Message", "메시지"]).desc(["Encouragement message", "격려 메시지"]),
|
|
241
|
+
showAt: t(["Show At", "표시 시간"]).desc(["When to show encouragement", "격려 표시 시점"]),
|
|
242
|
+
}))
|
|
243
|
+
.enum<Journey>("journey", (t) => ({
|
|
244
|
+
firstJoin: t(["First Join", "첫 가입"]).desc(["User just joined", "사용자가 방금 가입함"]),
|
|
245
|
+
waitPay: t(["Waiting Payment", "결제 대기"]).desc(["Awaiting payment", "결제 대기 중"]),
|
|
246
|
+
onProgress: t(["In Progress", "진행 중"]).desc(["Currently active", "현재 진행 중"]),
|
|
247
|
+
completed: t(["Completed", "완료됨"]).desc(["Journey completed", "여정 완료"]),
|
|
248
|
+
}))
|
|
249
|
+
.enum<Inquiry>("inquiry", (t) => ({
|
|
250
|
+
general: t(["General", "일반"]).desc(["General inquiry", "일반 문의"]),
|
|
251
|
+
support: t(["Support", "지원"]).desc(["Technical support", "기술 지원"]),
|
|
252
|
+
billing: t(["Billing", "청구"]).desc(["Billing inquiry", "청구 문의"]),
|
|
253
|
+
}));
|
|
254
|
+
```
|
|
323
255
|
|
|
324
|
-
|
|
325
|
-
profileHeading: ["User Profile", "사용자 프로필"],
|
|
326
|
-
accountSettings: ["Account Settings", "계정 설정"],
|
|
327
|
-
loginHistory: ["Login History", "로그인 기록"],
|
|
328
|
-
// * ==================== Custom ==================== * //
|
|
329
|
-
} satisfies ModelDictionary<User>;
|
|
256
|
+
## Pro Tips
|
|
330
257
|
|
|
331
|
-
|
|
332
|
-
|
|
258
|
+
- **TypeScript Errors**: TypeScript will show errors if you miss any fields or enum values - use this to your advantage!
|
|
259
|
+
- **Usage**: The dictionary is used for UI labels, form validation messages, and API documentation
|
|
260
|
+
- **Descriptions**: Keep descriptions concise but informative - they appear in tooltips and help text
|
|
261
|
+
- **Consistency**: Match the field order in dictionary with the constant file for easier maintenance
|
|
333
262
|
|
|
334
|
-
## Summary
|
|
263
|
+
## Summary
|
|
335
264
|
|
|
336
|
-
1. **
|
|
337
|
-
2. **
|
|
338
|
-
3. **
|
|
339
|
-
4. **
|
|
340
|
-
5. **
|
|
341
|
-
6. **
|
|
342
|
-
7. **
|
|
343
|
-
8. **Type Safety**: Use `satisfies ModelDictionary<YourModel>`
|
|
344
|
-
9. **Export**: Name the export to match the model name
|
|
345
|
-
10. **Completeness**: Ensure all fields and enums from the constant file are translated
|
|
265
|
+
1. **Import**: `scalarDictionary` from `@akanjs/dictionary`, types from constant file
|
|
266
|
+
2. **Initialize**: `scalarDictionary(["en", "ko"])`
|
|
267
|
+
3. **Scalar Info**: `.of((t) => t([...]).desc([...]))`
|
|
268
|
+
4. **Fields**: `.model<Type>((t) => ({ field: t([...]).desc([...]) }))`
|
|
269
|
+
5. **Enums**: `.enum<Type>("enumName", (t) => ({ value: t([...]).desc([...]) }))`
|
|
270
|
+
6. **Export**: `export const dictionary = ...`
|
|
271
|
+
7. **Format**: `t(["English", "Korean"]).desc(["English desc", "Korean desc"])`
|
|
346
272
|
|
|
347
|
-
Following these guidelines
|
|
273
|
+
Following these guidelines ensures your scalar dictionary files are complete, type-safe, and maintainable across the Akan.js framework.
|