@things-factory/i18n-base 10.1.79 → 10.1.106

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/i18n-base",
3
- "version": "10.1.79",
3
+ "version": "10.1.106",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -28,8 +28,8 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@operato/i18n": "^10.0.0",
31
- "@things-factory/auth-base": "^10.1.79",
32
- "@things-factory/shell": "^10.1.79"
31
+ "@things-factory/auth-base": "^10.1.106",
32
+ "@things-factory/shell": "^10.1.106"
33
33
  },
34
- "gitHead": "94cde9ba17dfdddb0a16139b71e5a7e370977e4a"
34
+ "gitHead": "8d78c14708420773ec32d18b8dd44c300771c44d"
35
35
  }
@@ -0,0 +1,135 @@
1
+ /*
2
+ * The terminology's AI 입력 declaration. The assistant's own validation is asked through its source file,
3
+ * which needs only `graphql` - the package keeps no run-time dependency on the assistant.
4
+ */
5
+ import { parse } from 'graphql'
6
+
7
+ import { clearEntryTargets, declareEntryTarget } from '../../ai-assistant/server/service/data-entry/entry-target'
8
+ import { terminologyChangeIssues, terminologyIssues } from '../server/ai-entry/terminology-entry-rules'
9
+ import { TERMINOLOGY_CATEGORIES, terminologyEntryTarget, terminologyEntryTargets } from '../server/ai-entry/terminology-entry-targets'
10
+
11
+ import en from '../translations/en.json'
12
+ import ja from '../translations/ja.json'
13
+ import ko from '../translations/ko.json'
14
+ import ms from '../translations/ms.json'
15
+ import zh from '../translations/zh.json'
16
+
17
+ const LANGUAGES: Record<string, Record<string, string>> = { en, ja, ko, ms, zh }
18
+
19
+ const rootFields = (document: string) => {
20
+ const operations = parse(document).definitions.filter((one: any) => one.kind === 'OperationDefinition') as any[]
21
+ expect(operations).toHaveLength(1)
22
+ return operations[0].selectionSet.selections.map((one: any) => one.name.value)
23
+ }
24
+
25
+ const row = (values: Record<string, unknown>, index = 0) => ({ index, values, sources: [], issues: [] })
26
+ const term = (locale: string, category: string, name: string, display = 'x') => row({ locale, category, name, display })
27
+
28
+ describe('terminology entry target', () => {
29
+ afterEach(() => clearEntryTargets())
30
+
31
+ it('is accepted by the assistant as it is declared', () => {
32
+ const [target] = terminologyEntryTargets({ locales: ['ko-KR', 'en-US'] })
33
+ expect(target.key).toBe('i18n-base.terminology')
34
+ expect(() => declareEntryTarget(target as any)).not.toThrow()
35
+ })
36
+
37
+ it('without a list given, the locales are the configured languages (or the framework five)', () => {
38
+ const locale = terminologyEntryTarget().fields.find(field => field.name === 'locale')!
39
+ expect(locale.type).toBe('enum')
40
+ expect(locale.values!.length).toBeGreaterThan(0)
41
+ expect(terminologyEntryTarget().fields.find(field => field.name === 'category')!.values).toEqual(TERMINOLOGY_CATEGORIES)
42
+ })
43
+
44
+ it('every document has one root field', () => {
45
+ const target = terminologyEntryTarget({ locales: ['ko-KR'] })
46
+ expect(rootFields(target.mutation)).toEqual(['createTerminology'])
47
+ expect(rootFields(target.update!.mutation)).toEqual(['updateTerminology'])
48
+ expect(rootFields(target.update!.read.query)).toEqual(['terminologies'])
49
+ expect(rootFields(target.update!.find!.query)).toEqual(['terminologies'])
50
+ })
51
+
52
+ it('create and patch carry the declared fields only', () => {
53
+ const target = terminologyEntryTarget({ locales: ['ko-KR'] })
54
+ expect(target.variables(row({ locale: 'ko-KR', category: 'label', name: 'department', display: '부서', domain: 'x', id: 'y' }))).toEqual({
55
+ terminology: { locale: 'ko-KR', category: 'label', name: 'department', display: '부서' }
56
+ })
57
+ const { patch } = target.update!.variables({ id: 't-1', record: { id: 't-1' }, values: { display: '부문', cuFlag: 'M', creator: { id: 'u' } } }) as any
58
+ expect(patch).toEqual({ display: '부문' })
59
+ expect(Object.keys(patch).every(name => target.update!.fields!.includes(name))).toBe(true)
60
+ })
61
+ })
62
+
63
+ describe('terminology checks', () => {
64
+ it('a (locale, category, name) the domain has keeps the row out; another locale or category does not', () => {
65
+ const issues = terminologyIssues([term('ko-KR', 'label', 'dept'), term('en-US', 'label', 'dept'), term('ko-KR', 'field', 'dept')], [
66
+ { locale: 'ko-KR', category: 'label', name: 'dept' }
67
+ ])
68
+ expect(issues[0]).toEqual([expect.objectContaining({ code: 'duplicate', blocking: true })])
69
+ expect(issues[1]).toEqual([])
70
+ expect(issues[2]).toEqual([])
71
+ })
72
+
73
+ it('a row repeating an earlier row of the proposal is kept out - the later one', () => {
74
+ const issues = terminologyIssues([term('ko-KR', 'label', 'dept', '부서'), term('ko-KR', 'label', 'dept', '부문')], [])
75
+ expect(issues[0]).toEqual([])
76
+ expect(issues[1]).toEqual([expect.objectContaining({ code: 'duplicate-in-proposal', blocking: true })])
77
+ })
78
+ })
79
+
80
+ describe('terminology change checks', () => {
81
+ const existing = [
82
+ { id: 't1', locale: 'ko-KR', category: 'label', name: 'dept' },
83
+ { id: 't2', locale: 'ko-KR', category: 'label', name: 'team' }
84
+ ]
85
+ const record = (id: string) => existing.find(one => one.id === id)!
86
+
87
+ it('a changed (locale, category, name) another term has keeps the change out; the term itself does not', () => {
88
+ const issues = terminologyChangeIssues(
89
+ [
90
+ { id: 't2', record: record('t2'), values: { name: 'dept' } },
91
+ { id: 't2', record: record('t2'), values: { locale: 'en-US', name: 'dept' } },
92
+ { id: 't2', record: record('t2'), values: { display: '팀' } }
93
+ ],
94
+ existing
95
+ )
96
+ expect(issues[0]).toEqual([expect.objectContaining({ code: 'duplicate', blocking: true })])
97
+ expect(issues[1]).toEqual([])
98
+ expect(issues[2]).toEqual([])
99
+ expect(terminologyChangeIssues([{ id: 't1', record: record('t1'), values: { category: 'label', display: 'x' } }], existing)).toEqual([[]])
100
+ })
101
+
102
+ it('two changes landing on the same key - the later one is kept out', () => {
103
+ const issues = terminologyChangeIssues(
104
+ [
105
+ { id: 't1', record: record('t1'), values: { name: 'new' } },
106
+ { id: 't2', record: record('t2'), values: { name: 'new' } }
107
+ ],
108
+ existing
109
+ )
110
+ expect(issues[0]).toEqual([])
111
+ expect(issues[1]).toEqual([expect.objectContaining({ code: 'duplicate-in-proposal', blocking: true })])
112
+ })
113
+
114
+ it('the update declares its check', () => {
115
+ expect(typeof terminologyEntryTarget({ locales: ['ko-KR'] }).update!.check).toBe('function')
116
+ })
117
+ })
118
+
119
+ describe('translations', () => {
120
+ it('the five languages have the same keys', () => {
121
+ const reference = Object.keys(en).sort()
122
+ for (const [language, table] of Object.entries(LANGUAGES)) expect({ language, keys: Object.keys(table).sort() }).toEqual({ language, keys: reference })
123
+ })
124
+
125
+ it('the label and messages the target names are translated in every language', () => {
126
+ const keys = [
127
+ terminologyEntryTarget({ locales: ['ko-KR'] }).labelKey,
128
+ ...terminologyIssues([term('ko-KR', 'label', 'a'), term('ko-KR', 'label', 'b'), term('ko-KR', 'label', 'b')], [{ locale: 'ko-KR', category: 'label', name: 'a' }])
129
+ .flat()
130
+ .map(issue => issue.messageKey!)
131
+ ]
132
+ expect(keys).toHaveLength(3)
133
+ for (const key of keys) for (const [language, table] of Object.entries(LANGUAGES)) expect({ language, key, has: key in table }).toEqual({ language, key, has: true })
134
+ })
135
+ })
@@ -3,8 +3,11 @@
3
3
  "field.category": "category",
4
4
  "field.display": "display",
5
5
  "field.locale": "locale",
6
+ "label.i18n-entry-terminology": "terminology",
6
7
  "text.NOTHING_SELECTED": "nothing is selected",
7
8
  "text.clearing-cache": "clearing cache",
9
+ "text.i18n-entry-terminology-exists": "a term with this locale, category and name already exists",
10
+ "text.i18n-entry-terminology-repeated": "same locale, category and name as an earlier row",
8
11
  "text.item view": "item view",
9
12
  "title.terminology": "terminology"
10
13
  }
@@ -3,8 +3,11 @@
3
3
  "field.category": "カテゴリー",
4
4
  "field.display": "表示",
5
5
  "field.locale": "ロケール",
6
+ "label.i18n-entry-terminology": "用語",
6
7
  "text.NOTHING_SELECTED": "選択されていません",
7
8
  "text.clearing-cache": "キャッシュをクリア",
9
+ "text.i18n-entry-terminology-exists": "同じロケール・カテゴリ・名前の用語がすでにあります",
10
+ "text.i18n-entry-terminology-repeated": "前の行とロケール・カテゴリ・名前が同じです",
8
11
  "text.item view": "項目表示",
9
12
  "title.terminology": "用語"
10
13
  }
@@ -3,8 +3,11 @@
3
3
  "field.category": "카테고리",
4
4
  "field.display": "표시",
5
5
  "field.locale": "로케일",
6
+ "label.i18n-entry-terminology": "용어",
6
7
  "text.NOTHING_SELECTED": "선택한 것이 없습니다",
7
8
  "text.clearing-cache": "캐시클리어",
9
+ "text.i18n-entry-terminology-exists": "같은 로케일·카테고리·이름의 용어가 이미 있습니다",
10
+ "text.i18n-entry-terminology-repeated": "앞 행과 로케일·카테고리·이름이 같습니다",
8
11
  "text.item view": "항목 보기",
9
12
  "title.terminology": "용어"
10
13
  }
@@ -3,8 +3,11 @@
3
3
  "field.category": "kategori",
4
4
  "field.display": "paparan",
5
5
  "field.locale": "lokaliti",
6
+ "label.i18n-entry-terminology": "istilah",
6
7
  "text.NOTHING_SELECTED": "tiada yang dipilih",
7
8
  "text.clearing-cache": "sedang mengosongkan cache",
9
+ "text.i18n-entry-terminology-exists": "istilah dengan lokal, kategori dan nama ini sudah wujud",
10
+ "text.i18n-entry-terminology-repeated": "lokal, kategori dan nama sama dengan baris sebelumnya",
8
11
  "text.item view": "paparan item",
9
12
  "title.terminology": "istilah"
10
13
  }
@@ -3,8 +3,11 @@
3
3
  "field.category": "类别",
4
4
  "field.display": "显示",
5
5
  "field.locale": "语言环境",
6
+ "label.i18n-entry-terminology": "术语",
6
7
  "text.NOTHING_SELECTED": "未选择任何项目",
7
8
  "text.clearing-cache": "正在清除缓存",
9
+ "text.i18n-entry-terminology-exists": "已有相同区域、类别和名称的术语",
10
+ "text.i18n-entry-terminology-repeated": "区域、类别和名称与前面的行相同",
8
11
  "text.item view": "项目视图",
9
12
  "title.terminology": "术语"
10
13
  }