@ihabdevteam/core 0.314.0 → 0.318.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/CHANGELOG.md +242 -0
- package/dist/components.cjs.js +182 -182
- package/dist/hooks.cjs.js +3 -3
- package/dist/i18n.cjs.js +3 -3
- package/dist/ihabdevteam-core.cjs.js +188 -188
- package/dist/ihabdevteam-core.d.ts +50 -3
- package/dist/packages/core/src/components/BarChart.js +16 -1
- package/dist/packages/core/src/components/Footer.js +1 -1
- package/dist/packages/core/src/components/HearingDeviceInfo.js +2 -2
- package/dist/packages/core/src/components/PanelRow.js +2 -2
- package/dist/packages/core/src/components/Popup/CouponInputPopup.js +4 -2
- package/dist/packages/core/src/components/Popup/DeviceSideTable.js +2 -2
- package/dist/packages/core/src/components/Popup/ListPopup.d.ts +12 -2
- package/dist/packages/core/src/components/Popup/WordDataUpdatePopup.js +1 -1
- package/dist/packages/core/src/components/Table.js +5 -3
- package/dist/packages/core/src/components/Tooltip/tooltipConstants.js +4 -0
- package/dist/packages/core/src/components/Training/TrainingBlockButton.js +5 -2
- package/dist/packages/core/src/components/Training/TrainingStepCard.js +11 -6
- package/dist/packages/core/src/components/Training/TrainingStepConfirmWithText.js +13 -8
- package/dist/packages/core/src/components/Training/audioWaits.js +22 -4
- package/dist/packages/core/src/components/Training/stepAnswerable.d.ts +32 -0
- package/dist/packages/core/src/components/Training/stepAnswerable.js +4 -0
- package/dist/packages/core/src/components/Training/useNoiseFade.js +5 -1
- package/dist/packages/core/src/components/WordTable.js +3 -16
- package/dist/packages/core/src/components/wordTableLayout.d.ts +34 -0
- package/dist/packages/core/src/components/wordTableLayout.js +48 -0
- package/dist/packages/core/src/components.d.ts +1 -1
- package/dist/packages/core/src/components.js +1 -1
- package/dist/packages/core/src/hooks/useWordTrainingFilter.js +6 -1
- package/dist/packages/core/src/i18n/locales/en.json +39 -18
- package/dist/packages/core/src/i18n/locales/ko.json +6 -1
- package/dist/packages/core/src/utils/wornDate.d.ts +14 -2
- package/dist/packages/core/src/utils/wornDate.js +38 -3
- package/dist/packages/core/src/version.d.ts +1 -1
- package/dist/packages/core/src/version.js +1 -1
- package/dist/utils.cjs.js +1 -1
- package/package.json +1 -1
|
@@ -117,6 +117,54 @@ export const POS_LABEL = {
|
|
|
117
117
|
export const WORD_TYPE_LABEL = {
|
|
118
118
|
'고유': '고유어', '한자': '한자어', '외래': '외래어', '혼종': '혼종어',
|
|
119
119
|
};
|
|
120
|
+
/* ── 품사·어종을 **지금 언어로** ─────────────────────────────────────────
|
|
121
|
+
위 두 표는 **한국어**다(자료의 약호를 한국어 전체 표기로 펴는 표). 화면에 그릴 때는 이것을 그대로
|
|
122
|
+
쓰면 안 된다 — 영어 화면에서 한 줄 안에 섞인다(말귀 실측 2026-10-10, 낱말 제안 카드:
|
|
123
|
+
`Grade 3 · 명사 · 혼종어 · … · Homonym 1` — 등급·동음이의는 영어인데 품사·어종만 한국어).
|
|
124
|
+
|
|
125
|
+
**그 번역은 `WordTable` 안에만 숨어 있었다.** 그래서 밖에서는 `POS_LABEL` 을 날로 쓸 수밖에
|
|
126
|
+
없었고(공개된 것이 그것뿐이었다), 앱이 그렇게 썼다. 번역을 여기로 올려 **같이 공개한다** —
|
|
127
|
+
같은 결정이 두 자리에 적히지 않게 한다((120), 2026-10-10).
|
|
128
|
+
|
|
129
|
+
두 날 표(`POS_LABEL`·`WORD_TYPE_LABEL`)는 **그대로 내보낸다** — 쓰는 데가 있고, 한국어 전체
|
|
130
|
+
표기가 필요한 자리(인쇄 등)도 있다. 다만 **화면에 그릴 때는 아래 둘을 쓴다.** */
|
|
131
|
+
/** 약호 → i18n 열쇠 조각. 자료의 `POS` 칸은 홑 코드 열셋(+ `명/관` 같은 슬래시 묶음)이다. */
|
|
132
|
+
const POS_I18N_KEY = {
|
|
133
|
+
'명': 'noun', '동': 'verb', '형': 'adjective', '부': 'adverb', '수': 'numeral', '관': 'determiner', '감': 'interjection',
|
|
134
|
+
'조': 'particle', '접': 'affix', '의': 'dependentNoun', '대': 'pronoun', '보동': 'auxVerb', '보형': 'auxAdjective',
|
|
135
|
+
};
|
|
136
|
+
/** 자료의 `Class` 칸은 넷이다(라이브 전수: 한자 9,361 · 고유 8,458 · 혼종 4,227 · 외래 954). */
|
|
137
|
+
const WORD_TYPE_I18N_KEY = { '고유': 'native', '한자': 'sino', '외래': 'foreign', '혼종': 'mixed' };
|
|
138
|
+
/**
|
|
139
|
+
* **품사 약호 → 지금 언어의 이름**(`{ '명': 'Noun', … }`). `expandSlashLabel` 에 그대로 넘긴다.
|
|
140
|
+
*
|
|
141
|
+
* 사전에 없는 약호는 한국어 전체 표기(`POS_LABEL`)로 둔다 — 자료에 새 약호가 와도 빈칸이 안 된다.
|
|
142
|
+
* @public
|
|
143
|
+
*/
|
|
144
|
+
export function posLabels(t) {
|
|
145
|
+
return Object.fromEntries(Object.entries(POS_LABEL).map(([k, v]) => [k, POS_I18N_KEY[k] ? t(`wordTable.pos.${POS_I18N_KEY[k]}`, { defaultValue: v }) : v]));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* **어종 약호 → 지금 언어의 이름**(`{ '한자': 'Sino-Korean', … }`). 쓰는 법은 `posLabels` 와 같다.
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
export function wordTypeLabels(t) {
|
|
152
|
+
return Object.fromEntries(Object.entries(WORD_TYPE_LABEL).map(([k, v]) => [k, WORD_TYPE_I18N_KEY[k] ? t(`wordSearch.filter.type.${WORD_TYPE_I18N_KEY[k]}`, { defaultValue: v }) : v]));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 약호 → i18n 열쇠 조각 표(품사·어종).
|
|
156
|
+
*
|
|
157
|
+
* **차례가 뜻을 갖는다**(2026-10-10, 말귀_2 가 쓰기 시작한 뒤 적음). 처음에는 *사전 덮임을 세는*
|
|
158
|
+
* 데 쓰라고 내보냈는데(약호마다 말이 있나), 말귀가 **제안 폼 고르개의 차례**를 여기서 가져갔다 —
|
|
159
|
+
* *"말과 무관한 것은 말에서 안 꺼낸다"* 는 가름이고 맞다. 그래서 이 표는 이제 **두 가지를 뜻한다**:
|
|
160
|
+
* *어떤 약호가 있나* 와 *어떤 차례로 보이나.*
|
|
161
|
+
*
|
|
162
|
+
* **차례는 한국어 사전 차례다**(명 · 동 · 형 · 부 · 수 · 관 · 감 · 조 · 접 · 의 · 대 · 보동 · 보형).
|
|
163
|
+
* 약호를 **더하는 것은 뒤에 붙이면** 쓰는 쪽이 안 흔들리지만, **차례를 바꾸는 것은 쓰는 쪽 화면을
|
|
164
|
+
* 바꾼다** — 판 올릴 때 알려야 한다. *한 칸이 두 가지를 뜻하는 것은 어긋나는 날까지 안 보인다.*
|
|
165
|
+
* @public
|
|
166
|
+
*/
|
|
167
|
+
export const WORD_LABEL_I18N_KEYS = { pos: POS_I18N_KEY, wordType: WORD_TYPE_I18N_KEY };
|
|
120
168
|
/**
|
|
121
169
|
* 슬래시로 묶인 복합 약호(예: '명/관', '동/보동')를 라벨맵으로 각각 펼쳐 다시 '/'로 잇는다.
|
|
122
170
|
* 라벨맵에 없는 조각은 원문 그대로 둔다. 빈 문자열은 빈 문자열 그대로 반환.
|
|
@@ -219,7 +219,7 @@ export { default as TrainingPagePopupTitle } from './components/Training/Trainin
|
|
|
219
219
|
export { default as FilterPanel, FILTER_ALL_VALUE } from './components/FilterPanel';
|
|
220
220
|
export type { FilterPanelProps, FilterPanelSection, FilterPanelItem } from './components/FilterPanel';
|
|
221
221
|
export { default as WordTable, WORD_TABLE_PAPER } from './components/WordTable';
|
|
222
|
-
export { POS_LABEL, WORD_TYPE_LABEL, expandSlashLabel, printWordTable } from './components/wordTableLayout';
|
|
222
|
+
export { POS_LABEL, WORD_TYPE_LABEL, posLabels, wordTypeLabels, WORD_LABEL_I18N_KEYS, expandSlashLabel, printWordTable } from './components/wordTableLayout';
|
|
223
223
|
export type { WordTableProps, WordTableOrientation, WordTableContentType, WordTableCutLine, WordTableEditEntry } from './components/WordTable';
|
|
224
224
|
export { useFitScale } from './components/useFitScale';
|
|
225
225
|
export type { UseFitScaleResult } from './components/useFitScale';
|
|
@@ -161,7 +161,7 @@ export { default as FilterPanel, FILTER_ALL_VALUE } from './components/FilterPan
|
|
|
161
161
|
export { default as WordTable, WORD_TABLE_PAPER } from './components/WordTable';
|
|
162
162
|
// 라벨 맵·헬퍼는 순수 상수/함수라 컴포넌트 파일(WordTable.tsx)을 거치지 않고 직접 내보낸다
|
|
163
163
|
// (컴포넌트 파일이 non-component를 export하면 fast refresh가 깨진다).
|
|
164
|
-
export { POS_LABEL, WORD_TYPE_LABEL, expandSlashLabel, printWordTable } from './components/wordTableLayout';
|
|
164
|
+
export { POS_LABEL, WORD_TYPE_LABEL, posLabels, wordTypeLabels, WORD_LABEL_I18N_KEYS, expandSlashLabel, printWordTable } from './components/wordTableLayout';
|
|
165
165
|
export { useFitScale } from './components/useFitScale';
|
|
166
166
|
export { default as WordSearchResultBlock } from './components/Word/WordSearchResultBlock';
|
|
167
167
|
export { default as PhonemeSelectBlock } from './components/Word/PhonemeSelectBlock';
|
|
@@ -414,7 +414,12 @@ export function useWordTrainingFilter(groupKey, caps, opts) {
|
|
|
414
414
|
// label만 오버라이드하고 나머지(items/allOption 등)는 공용 빌더 그대로 사용.
|
|
415
415
|
sections.push({
|
|
416
416
|
...buildGradeSection(t, attrKeys, {
|
|
417
|
-
|
|
417
|
+
/* **등급마다 무엇인지 적는다**(말귀 요청 (205), 사장님이 주신 글 — 2026-10-12). 전에는
|
|
418
|
+
*"국립국어원의 기초 어휘 등급이 결과에 반영됩니다"* 한 줄이라, 1등급이 쉬운 쪽인지
|
|
419
|
+
어려운 쪽인지도 알 수 없었다. 앱 두 화면에는 이미 이 다섯 줄이 들어가 있어
|
|
420
|
+
(앱 열쇠 `wordSearch.filter.gradeInfo`), 이 자리만 한 줄로 남아 **세 화면이 다른 말**을
|
|
421
|
+
하고 있었다 — 글자까지 같은 것을 쓴다. */
|
|
422
|
+
labelInfo: infoAction([1, 2, 3, 4, 5].map(n => t(`wordPairs.filter.difficultyInfo${n}`))),
|
|
418
423
|
}),
|
|
419
424
|
label: LABELS.gradeLabel,
|
|
420
425
|
});
|
|
@@ -30,9 +30,11 @@
|
|
|
30
30
|
"reset": "Reset",
|
|
31
31
|
"update": "Update",
|
|
32
32
|
"download": "Download",
|
|
33
|
-
"
|
|
33
|
+
"likeCount_one": "{{count}} like",
|
|
34
|
+
"likeCount_other": "{{count}} likes",
|
|
34
35
|
"selectedOfMax": "{{count}} of {{max}}",
|
|
35
|
-
"
|
|
36
|
+
"downloadCount_one": "{{count}} download",
|
|
37
|
+
"downloadCount_other": "{{count}} downloads",
|
|
36
38
|
"descriptionFor": "About {{name}}",
|
|
37
39
|
"search": "Search",
|
|
38
40
|
"empty": "No data.",
|
|
@@ -124,7 +126,8 @@
|
|
|
124
126
|
"noVisit": "No Show"
|
|
125
127
|
},
|
|
126
128
|
"periodSelect": "Period unit",
|
|
127
|
-
"
|
|
129
|
+
"visitCount_one": "{{count}} visit",
|
|
130
|
+
"visitCount_other": "{{count}} visits",
|
|
128
131
|
"statusCount": "{{count}} {{label}}"
|
|
129
132
|
},
|
|
130
133
|
"passCard": {
|
|
@@ -196,7 +199,8 @@
|
|
|
196
199
|
"more": "More",
|
|
197
200
|
"moreMenu": "More menu",
|
|
198
201
|
"badgeLabel": "{{label}}, {{count}}",
|
|
199
|
-
"
|
|
202
|
+
"badgeInsideLabel_one": "{{label}}, {{count}} notification inside",
|
|
203
|
+
"badgeInsideLabel_other": "{{label}}, {{count}} notifications inside"
|
|
200
204
|
},
|
|
201
205
|
"settings": {
|
|
202
206
|
"language": {
|
|
@@ -356,8 +360,10 @@
|
|
|
356
360
|
"sameStructure": "Only same phoneme structure"
|
|
357
361
|
},
|
|
358
362
|
"noQuestions": "No questions available.",
|
|
359
|
-
"
|
|
360
|
-
"
|
|
363
|
+
"countQuestions_one": "{{n}} question",
|
|
364
|
+
"countQuestions_other": "{{n}} questions",
|
|
365
|
+
"countSentences_one": "{{n}} sentence",
|
|
366
|
+
"countSentences_other": "{{n}} sentences",
|
|
361
367
|
"comingSoon": "Coming Soon",
|
|
362
368
|
"nextQuestion": "Next",
|
|
363
369
|
"noiseRatioTitle": "Noise Ratio",
|
|
@@ -789,7 +795,8 @@
|
|
|
789
795
|
"title": "Load Saved Word Table",
|
|
790
796
|
"empty": "No saved word tables.",
|
|
791
797
|
"loadButton": "Load",
|
|
792
|
-
"
|
|
798
|
+
"itemCount_one": "{{count}} word",
|
|
799
|
+
"itemCount_other": "{{count}} words",
|
|
793
800
|
"deleteAriaLabel": "Delete {{name}}",
|
|
794
801
|
"viewLabel": "View as",
|
|
795
802
|
"sortLabel": "Sort by",
|
|
@@ -841,14 +848,18 @@
|
|
|
841
848
|
"none": "Tap ○ · △ · ✕ under a cell to mark it. With a mouse they appear when you hover the cell; with a keyboard, when focus reaches it.",
|
|
842
849
|
"tally": "{{marked}} scored · ○ {{o}} · △ {{triangle}} · ✕ {{x}} · {{rate}}%",
|
|
843
850
|
"save": "Save score",
|
|
844
|
-
"
|
|
851
|
+
"saved_one": "Saved {{count}} mark for {{name}}.",
|
|
852
|
+
"saved_other": "Saved {{count}} marks for {{name}}.",
|
|
845
853
|
"reviewTitle": "Create a review table?",
|
|
846
|
-
"
|
|
854
|
+
"reviewMessage_one": "Builds a new table from the {{count}} cell {{name}} got wrong or partial. Correct ones are left out.",
|
|
855
|
+
"reviewMessage_other": "Builds a new table from the {{count}} cells {{name}} got wrong or partial. Correct ones are left out.",
|
|
847
856
|
"reviewConfirm": "Create review table",
|
|
848
857
|
"reviewDescription": "Cells marked wrong or partial in “{{name}}” — {{count}} of them.",
|
|
849
|
-
"
|
|
858
|
+
"reviewCreated_one": "Created “{{name}}” — {{count}} cell.",
|
|
859
|
+
"reviewCreated_other": "Created “{{name}}” — {{count}} cells.",
|
|
850
860
|
"discardTitle": "Discard this scoring?",
|
|
851
|
-
"
|
|
861
|
+
"discardMessage_one": "You marked {{count}} cell but have not saved. Leaving discards it.",
|
|
862
|
+
"discardMessage_other": "You marked {{count}} cells but have not saved. Leaving discards them.",
|
|
852
863
|
"discardConfirm": "Discard and leave"
|
|
853
864
|
},
|
|
854
865
|
"info": {
|
|
@@ -1012,7 +1023,8 @@
|
|
|
1012
1023
|
"copy": "Copy",
|
|
1013
1024
|
"loading": "Loading…",
|
|
1014
1025
|
"empty": "No links created yet.",
|
|
1015
|
-
"
|
|
1026
|
+
"uses_one": "Used {{count}} time",
|
|
1027
|
+
"uses_other": "Used {{count}} times",
|
|
1016
1028
|
"expiresAt": "Expires: {{date}}",
|
|
1017
1029
|
"createdAt": "Created: {{date}}",
|
|
1018
1030
|
"confirmDelete": "Delete this link?",
|
|
@@ -1133,7 +1145,11 @@
|
|
|
1133
1145
|
],
|
|
1134
1146
|
"orderAny": "Any",
|
|
1135
1147
|
"orderMatch": "Match",
|
|
1136
|
-
"
|
|
1148
|
+
"difficultyInfo1": "Level 1 — everyday vocabulary acquired before school age and used for life",
|
|
1149
|
+
"difficultyInfo2": "Level 2 — difficulty of grades 1–2",
|
|
1150
|
+
"difficultyInfo3": "Level 3 — difficulty of grades 3–4",
|
|
1151
|
+
"difficultyInfo4": "Level 4 — difficulty of grades 5–6",
|
|
1152
|
+
"difficultyInfo5": "No level — frequent everyday words outside the national standard list, plus coinages and loanwords",
|
|
1137
1153
|
"selectLabel": "Select",
|
|
1138
1154
|
"onlyHasImage": "Only words with images",
|
|
1139
1155
|
"onlySameStructure": "Only same phoneme structure"
|
|
@@ -1230,7 +1246,7 @@
|
|
|
1230
1246
|
"address": "Address",
|
|
1231
1247
|
"support": "Support",
|
|
1232
1248
|
"ceoName": "Ji-hye Han",
|
|
1233
|
-
"addressValue": "302, 3F, Haechan Bldg, 32
|
|
1249
|
+
"addressValue": "302, 3F, Haechan Bldg, 32 Sicheong-gil, Chuncheon, Gangwon, Korea"
|
|
1234
1250
|
},
|
|
1235
1251
|
"landing": {
|
|
1236
1252
|
"trainingPhone": {
|
|
@@ -1274,7 +1290,8 @@
|
|
|
1274
1290
|
"phaseWordDownload": "Download Word Data",
|
|
1275
1291
|
"phaseImageCheck": "Check Images",
|
|
1276
1292
|
"phasePairAnalysis": "Analyze Word Pairs",
|
|
1277
|
-
"
|
|
1293
|
+
"downloadingCount_one": "Downloading {{n}} word",
|
|
1294
|
+
"downloadingCount_other": "Downloading {{n}} words",
|
|
1278
1295
|
"checkingPage": "Checking page {{page}} list",
|
|
1279
1296
|
"imageCount": "{{done}} / {{total}} items"
|
|
1280
1297
|
},
|
|
@@ -1334,7 +1351,8 @@
|
|
|
1334
1351
|
"countSuffix": " sessions"
|
|
1335
1352
|
},
|
|
1336
1353
|
"time": {
|
|
1337
|
-
"
|
|
1354
|
+
"countLabel_one": "{{n}} session",
|
|
1355
|
+
"countLabel_other": "{{n}} sessions",
|
|
1338
1356
|
"empty": "No training records"
|
|
1339
1357
|
},
|
|
1340
1358
|
"daily": {
|
|
@@ -1349,6 +1367,7 @@
|
|
|
1349
1367
|
"title": "Linked Visit Records"
|
|
1350
1368
|
},
|
|
1351
1369
|
"detail": "Details",
|
|
1370
|
+
"detailFor": "{{name}} details",
|
|
1352
1371
|
"trainingType": {
|
|
1353
1372
|
"LISTEN_IN_NOISE": "Sentence in Noise",
|
|
1354
1373
|
"SHORT_STORY": "Short Story",
|
|
@@ -1397,7 +1416,8 @@
|
|
|
1397
1416
|
"colTrend": "Change",
|
|
1398
1417
|
"colRuns": "Runs",
|
|
1399
1418
|
"unitItem": " items",
|
|
1400
|
-
"
|
|
1419
|
+
"skippedOnly_one": "{{n}} older record was excluded — it does not say what was answered.",
|
|
1420
|
+
"skippedOnly_other": "{{n}} older records were excluded — they do not say what was answered.",
|
|
1401
1421
|
"summaryLabel": "Recorded",
|
|
1402
1422
|
"summaryValue": "{{items}} items · {{pending}} still missed",
|
|
1403
1423
|
"nextLabel": "Next to work on",
|
|
@@ -1407,7 +1427,8 @@
|
|
|
1407
1427
|
"typeClear": "All caught up",
|
|
1408
1428
|
"typePending": "{{pending}} of {{total}} left",
|
|
1409
1429
|
"flat": "no change",
|
|
1410
|
-
"
|
|
1430
|
+
"runs_one": "{{count}} run",
|
|
1431
|
+
"runs_other": "{{count}} runs",
|
|
1411
1432
|
"avgDuration": "{{sec}}s avg"
|
|
1412
1433
|
},
|
|
1413
1434
|
"table": {
|
|
@@ -1134,7 +1134,11 @@
|
|
|
1134
1134
|
],
|
|
1135
1135
|
"orderAny": "무관",
|
|
1136
1136
|
"orderMatch": "일치",
|
|
1137
|
-
"
|
|
1137
|
+
"difficultyInfo1": "1등급 : 학령전기에 습득하여 평생에 걸쳐 일상 언어생활을 하는 데 사용되는 어휘",
|
|
1138
|
+
"difficultyInfo2": "2등급 : 초등학교 1-2학년 난이도 어휘",
|
|
1139
|
+
"difficultyInfo3": "3등급 : 초등학교 3-4학년 난이도 어휘",
|
|
1140
|
+
"difficultyInfo4": "4등급 : 초등학교 5-6학년 난이도 어휘",
|
|
1141
|
+
"difficultyInfo5": "등급없음 : 국립국어원 표준 어휘 외 고빈도 생활 어휘, 신조어 및 외래어",
|
|
1138
1142
|
"selectLabel": "선택",
|
|
1139
1143
|
"onlyHasImage": "이미지 있는 단어만",
|
|
1140
1144
|
"onlySameStructure": "음소 구조 같은 단어만"
|
|
@@ -1350,6 +1354,7 @@
|
|
|
1350
1354
|
"title": "방문 연동 기록"
|
|
1351
1355
|
},
|
|
1352
1356
|
"detail": "상세보기",
|
|
1357
|
+
"detailFor": "{{name}} 상세보기",
|
|
1353
1358
|
"trainingType": {
|
|
1354
1359
|
"LISTEN_IN_NOISE": "소음 하 문장",
|
|
1355
1360
|
"SHORT_STORY": "짧은 이야기",
|
|
@@ -5,8 +5,20 @@
|
|
|
5
5
|
* `2026.null.null` 이 찍혔고, 정렬 열쇠를 만들 때는 `null.padStart` 에서 표가 통째로 터졌다. 빈 값은 '' 로 다룬다.
|
|
6
6
|
*/
|
|
7
7
|
type 칸값 = string | number | null | undefined;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* 화면에 적을 착용일.
|
|
10
|
+
*
|
|
11
|
+
* **`lang` 을 주면 보는 말을 따른다**(말귀 요청 (118), 사장님 지시 2026-10-11 *"날짜 꼴도 하나로
|
|
12
|
+
* 통일해"*). 한국어 `2026년 9월 9일` · 영어 `Sep 9, 2026` — 앱이 다른 화면 날짜를 모은 그 꼴이고,
|
|
13
|
+
* 코어 `formatDateLocale` 이 내는 것과 같다(글자를 또 짓지 않고 그것에 넘긴다).
|
|
14
|
+
*
|
|
15
|
+
* **연·월·일이 다 있을 때만** 그렇게 한다. 반쪽 값(연도만 · 연월만)은 그 말로 옮길 수가 없어
|
|
16
|
+
* 예전처럼 점으로 잇는다(`2026` · `2026.9`) — 서버에 월·일이 `null` 로 오는 옛 기록이 있다.
|
|
17
|
+
*
|
|
18
|
+
* `lang` 을 안 주면 **예전 그대로** 점 꼴이다(`2026.09.09`) — 부르는 자리가 말을 모르는 곳도 있을
|
|
19
|
+
* 수 있어 깨지지 않게 둔다. 화면에 그리는 자리는 주는 쪽이 맞다.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatWornDate(year: 칸값, month: 칸값, day: 칸값, lang?: string): string;
|
|
10
22
|
/** 착용일 정렬 열쇠 — 자리수를 채워(`2026-05-02`) 글자 비교가 날짜 차례와 같게. 연도가 없으면 ''. */
|
|
11
23
|
export declare function wornDateSortKey(year: 칸값, month: 칸값, day: 칸값): string;
|
|
12
24
|
export {};
|
|
@@ -1,7 +1,42 @@
|
|
|
1
|
+
import { formatDateLocale } from './date';
|
|
1
2
|
const 글자 = (v) => (v == null ? '' : String(v).trim());
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* 화면에 적을 착용일.
|
|
5
|
+
*
|
|
6
|
+
* **`lang` 을 주면 보는 말을 따른다**(말귀 요청 (118), 사장님 지시 2026-10-11 *"날짜 꼴도 하나로
|
|
7
|
+
* 통일해"*). 한국어 `2026년 9월 9일` · 영어 `Sep 9, 2026` — 앱이 다른 화면 날짜를 모은 그 꼴이고,
|
|
8
|
+
* 코어 `formatDateLocale` 이 내는 것과 같다(글자를 또 짓지 않고 그것에 넘긴다).
|
|
9
|
+
*
|
|
10
|
+
* **연·월·일이 다 있을 때만** 그렇게 한다. 반쪽 값(연도만 · 연월만)은 그 말로 옮길 수가 없어
|
|
11
|
+
* 예전처럼 점으로 잇는다(`2026` · `2026.9`) — 서버에 월·일이 `null` 로 오는 옛 기록이 있다.
|
|
12
|
+
*
|
|
13
|
+
* `lang` 을 안 주면 **예전 그대로** 점 꼴이다(`2026.09.09`) — 부르는 자리가 말을 모르는 곳도 있을
|
|
14
|
+
* 수 있어 깨지지 않게 둔다. 화면에 그리는 자리는 주는 쪽이 맞다.
|
|
15
|
+
*/
|
|
16
|
+
export function formatWornDate(year, month, day, lang) {
|
|
17
|
+
const y = 글자(year), m = 글자(month), d = 글자(day);
|
|
18
|
+
if (lang && y && m && d) {
|
|
19
|
+
/* 자리수를 채워 ISO 로 만든다 — `formatDateLocale` 이 읽는 꼴. */
|
|
20
|
+
const iso = `${y.padStart(4, '0')}-${m.padStart(2, '0')}-${d.padStart(2, '0')}`;
|
|
21
|
+
/**
|
|
22
|
+
* **되돌아오는지 본다 — `new Date` 는 없는 날짜를 조용히 굴린다.**
|
|
23
|
+
* `2026-09-31` → **2026-10-01**, `2026-02-30` → **2026-03-02**(실측). 그러면 자료에 적힌 날과
|
|
24
|
+
* **다른 날**이 화면에 선다 — 잘못된 값을 보여 주는 것보다 **조용히 고쳐 보여 주는 것**이 나쁘다
|
|
25
|
+
* (사람이 틀린 줄 모른다). 지은 ISO 가 그대로 돌아올 때만 말로 옮기고, 아니면 아래 점 꼴로
|
|
26
|
+
* 자료에 적힌 그대로 보여 준다.
|
|
27
|
+
*
|
|
28
|
+
* *이 가름은 못이 안 울어서 찾았다* — `&& m && d` 조건만 깨뜨려 봤을 때는 조용했다(그때는
|
|
29
|
+
* `2026-00-00` 이 **못 읽는 날짜**라 저절로 떨어졌다). 못이 안 울면 **무엇이 막고 있나**를 묻는다.
|
|
30
|
+
*/
|
|
31
|
+
const 날 = new Date(iso);
|
|
32
|
+
const 되돌아옴 = !Number.isNaN(날.getTime()) && 날.toISOString().slice(0, 10) === iso;
|
|
33
|
+
if (되돌아옴) {
|
|
34
|
+
const 말대로 = formatDateLocale(iso, lang);
|
|
35
|
+
if (말대로)
|
|
36
|
+
return 말대로;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const 조각 = [y, m, d];
|
|
5
40
|
while (조각.length > 0 && 조각[조각.length - 1] === '')
|
|
6
41
|
조각.pop();
|
|
7
42
|
return 조각.join('.');
|