@things-factory/gallery 10.1.234 → 10.1.236

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.
Files changed (58) hide show
  1. package/client/bootstrap.ts +12 -0
  2. package/client/components/gallery-saver-setting-let.ts +78 -0
  3. package/client/components/gallery-show.ts +312 -0
  4. package/client/components/gallery-uploader.ts +16 -1
  5. package/client/components/gallery-viewer.ts +17 -4
  6. package/client/pages/gallery-page.ts +18 -16
  7. package/client/utils/gallery-api.ts +4 -2
  8. package/client/utils/gallery-saver.ts +72 -0
  9. package/dist-client/bootstrap.d.ts +1 -0
  10. package/dist-client/bootstrap.js +10 -0
  11. package/dist-client/bootstrap.js.map +1 -1
  12. package/dist-client/components/gallery-saver-setting-let.d.ts +11 -0
  13. package/dist-client/components/gallery-saver-setting-let.js +81 -0
  14. package/dist-client/components/gallery-saver-setting-let.js.map +1 -0
  15. package/dist-client/components/gallery-show.d.ts +46 -0
  16. package/dist-client/components/gallery-show.js +316 -0
  17. package/dist-client/components/gallery-show.js.map +1 -0
  18. package/dist-client/components/gallery-uploader.d.ts +2 -0
  19. package/dist-client/components/gallery-uploader.js +20 -1
  20. package/dist-client/components/gallery-uploader.js.map +1 -1
  21. package/dist-client/components/gallery-viewer.d.ts +2 -2
  22. package/dist-client/components/gallery-viewer.js +16 -3
  23. package/dist-client/components/gallery-viewer.js.map +1 -1
  24. package/dist-client/pages/gallery-page.d.ts +0 -1
  25. package/dist-client/pages/gallery-page.js +20 -16
  26. package/dist-client/pages/gallery-page.js.map +1 -1
  27. package/dist-client/tsconfig.tsbuildinfo +1 -1
  28. package/dist-client/utils/gallery-api.d.ts +4 -0
  29. package/dist-client/utils/gallery-api.js +1 -1
  30. package/dist-client/utils/gallery-api.js.map +1 -1
  31. package/dist-client/utils/gallery-saver.d.ts +6 -0
  32. package/dist-client/utils/gallery-saver.js +69 -0
  33. package/dist-client/utils/gallery-saver.js.map +1 -0
  34. package/dist-server/service/gallery/gallery-resolver.js +11 -2
  35. package/dist-server/service/gallery/gallery-resolver.js.map +1 -1
  36. package/dist-server/service/gallery/gallery-rules.d.ts +4 -2
  37. package/dist-server/service/gallery/gallery-rules.js +4 -3
  38. package/dist-server/service/gallery/gallery-rules.js.map +1 -1
  39. package/dist-server/service/gallery/gallery-types.d.ts +3 -0
  40. package/dist-server/service/gallery/gallery-types.js +12 -0
  41. package/dist-server/service/gallery/gallery-types.js.map +1 -1
  42. package/dist-server/service/gallery/gallery.d.ts +2 -1
  43. package/dist-server/service/gallery/gallery.js +7 -2
  44. package/dist-server/service/gallery/gallery.js.map +1 -1
  45. package/dist-server/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +5 -4
  47. package/server/service/gallery/gallery-resolver.ts +9 -2
  48. package/server/service/gallery/gallery-rules.ts +5 -4
  49. package/server/service/gallery/gallery-types.ts +9 -0
  50. package/server/service/gallery/gallery.ts +7 -2
  51. package/test/gallery-persistence.test.ts +24 -3
  52. package/test/gallery-rules.test.ts +10 -4
  53. package/translations/en.json +15 -2
  54. package/translations/es.json +15 -2
  55. package/translations/ja.json +15 -2
  56. package/translations/ko.json +15 -2
  57. package/translations/ms.json +15 -2
  58. package/translations/zh.json +15 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/gallery",
3
- "version": "10.1.234",
3
+ "version": "10.1.236",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -38,13 +38,14 @@
38
38
  "@things-factory/apptool-base": "^10.1.234",
39
39
  "@things-factory/attachment-base": "^10.1.234",
40
40
  "@things-factory/auth-base": "^10.1.234",
41
- "@things-factory/calendar": "^10.1.234",
41
+ "@things-factory/calendar": "^10.1.236",
42
42
  "@things-factory/i18n-base": "^10.1.234",
43
- "@things-factory/notification": "^10.1.234",
43
+ "@things-factory/notification": "^10.1.236",
44
+ "@things-factory/setting-base": "^10.1.234",
44
45
  "@things-factory/shell": "^10.1.234",
45
46
  "graphql-tag": "^2.12.6",
46
47
  "graphql-upload": "^15.0.2",
47
48
  "lit": "^3.1.2"
48
49
  },
49
- "gitHead": "92f29853680fff527a5f1d167ab3ad9b27837ba9"
50
+ "gitHead": "1560112ae8ae5a5d296b4fce1d5a1be05e30e8c2"
50
51
  }
@@ -131,13 +131,14 @@ async function seenPosts(context: ResolverContext) {
131
131
  const qb = getRepository(GalleryPost).createQueryBuilder('P').where('P.domain = :domain', { domain: domain.id })
132
132
  if (!viewer.moderator) qb.andWhere('(P.hiddenAt IS NULL OR P.creator = :me)', { me: user.id })
133
133
  const active = [...viewer.activePeople]
134
- /* The own gallery of someone who left is no longer shown (decision 5). */
134
+ /* The own gallery is its owner's until shared (decision 2), and no longer shown when they left (decision 5). */
135
135
  qb.andWhere(
136
136
  active.length
137
- ? '(P.space != :personal OR P.creator = :me OR P.creator IN (:...active))'
137
+ ? '(P.space != :personal OR P.creator = :me OR (P.shared = :shared AND P.creator IN (:...active)))'
138
138
  : '(P.space != :personal OR P.creator = :me)',
139
139
  {
140
140
  personal: GallerySpace.PERSONAL,
141
+ shared: true,
141
142
  me: user.id,
142
143
  active
143
144
  }
@@ -164,6 +165,10 @@ export class GalleryResolver {
164
165
  if (filter?.space) qb.andWhere('P.space = :space', { space: filter.space })
165
166
  if (filter?.eventId) qb.andWhere('P.event = :event', { event: filter.eventId })
166
167
  if (filter?.authorId) qb.andWhere('P.creator = :author', { author: filter.authorId })
168
+ /* The own gallery is the asker's, known here - the client's profile carries no user id (2026-10-31). */
169
+ if (filter?.mine) qb.andWhere('P.creator = :me', { me: context.state.user.id })
170
+ /* What a shared screen may show - the screen saver (decision 9): never one's own unshared photos. */
171
+ if (filter?.everyone) qb.andWhere('(P.space != :personal OR P.shared = :shared)', { personal: GallerySpace.PERSONAL, shared: true })
167
172
  const tag = tagOf(filter?.tag ?? '')
168
173
  /* Tags are kept as a JSON list: the quotes bound the word, so 「워크숍」 does not find 「워크숍2026」. */
169
174
  if (tag) qb.andWhere('LOWER(P.tags) LIKE :tag', { tag: `%${JSON.stringify(tag.toLowerCase())}%` })
@@ -291,6 +296,7 @@ export class GalleryResolver {
291
296
  const post = await getRepository(GalleryPost, tx).save({
292
297
  domain,
293
298
  space: eventId ? GallerySpace.COMPANY : (input.space ?? GallerySpace.COMPANY),
299
+ shared: !!input.shared,
294
300
  caption: input.caption?.trim() || null,
295
301
  takenAt: input.takenAt ?? new Date(),
296
302
  tags: tagsOf(input.tags),
@@ -331,6 +337,7 @@ export class GalleryResolver {
331
337
  : null
332
338
  await getRepository(GalleryPost, tx).update(post.id, {
333
339
  space: eventId ? GallerySpace.COMPANY : (input.space ?? post.space),
340
+ shared: input.shared ?? post.shared,
334
341
  caption: input.caption?.trim() || null,
335
342
  takenAt: input.takenAt ?? post.takenAt,
336
343
  tags: tagsOf(input.tags),
@@ -100,16 +100,17 @@ export function tagCounts(
100
100
  }
101
101
 
102
102
  export type Viewer = { userId: string; moderator: boolean; activePeople: ReadonlySet<string> }
103
- export type SeenPost = { space: string; creatorId?: string | null; hiddenAt?: Date | string | null }
103
+ export type SeenPost = { space: string; shared?: boolean | null; creatorId?: string | null; hiddenAt?: Date | string | null }
104
104
 
105
105
  /**
106
- * Whether the viewer sees the post. Everyone of the domain sees every post (decision 2), except a hidden one - its
107
- * owner and the moderators see that - and the own gallery of someone who left (decision 5).
106
+ * Whether the viewer sees the post. Everyone of the domain sees the company's posts (decision 2) and the own-gallery
107
+ * posts their owner shared (decision 2, 2026-10-31) - a moderator sees no unshared one either. A hidden post is seen by
108
+ * its owner and the moderators; the own gallery of someone who left, by no one else (decision 5).
108
109
  */
109
110
  export function seesPost(post: SeenPost, viewer: Viewer): boolean {
110
111
  const own = !!post.creatorId && post.creatorId === viewer.userId
111
112
  if (post.hiddenAt && !own && !viewer.moderator) return false
112
- if (post.space === 'PERSONAL' && !own && !viewer.activePeople.has(post.creatorId ?? '')) return false
113
+ if (post.space === 'PERSONAL' && !own && !(post.shared && viewer.activePeople.has(post.creatorId ?? ''))) return false
113
114
  return true
114
115
  }
115
116
 
@@ -107,6 +107,9 @@ export class GalleryPostInput {
107
107
  @Field(type => GallerySpace, { nullable: true, description: "The company's gallery (default) or the person's own." })
108
108
  space?: GallerySpace
109
109
 
110
+ @Field({ nullable: true, description: "Shares a post of the own gallery with everyone of the domain; it is the person's alone when not given." })
111
+ shared?: boolean
112
+
110
113
  @Field({ nullable: true, description: 'A short caption, plain text.' })
111
114
  caption?: string
112
115
 
@@ -133,6 +136,12 @@ export class GalleryFilter {
133
136
 
134
137
  @Field({ nullable: true, description: "Only this person's posts." })
135
138
  authorId?: string
139
+
140
+ @Field({ nullable: true, description: "Only the asking person's own posts - their own gallery." })
141
+ mine?: boolean
142
+
143
+ @Field({ nullable: true, description: "Only the posts everyone of the domain sees - the company's and the shared own ones." })
144
+ everyone?: boolean
136
145
  }
137
146
 
138
147
  @InputType({ description: 'An event made from the gallery.' })
@@ -20,7 +20,7 @@ import { User } from '@things-factory/auth-base'
20
20
  import { Event } from '@things-factory/calendar'
21
21
  import { Domain } from '@things-factory/shell'
22
22
 
23
- /** Where a post lives. Both are seen by everyone of the domain - the space says only whose it is and where it shows. */
23
+ /** Where a post lives. The company's is seen by everyone of the domain; the person's own by them, and by everyone once shared. */
24
24
  export enum GallerySpace {
25
25
  /** The company's - events, workshops and work. */
26
26
  COMPANY = 'COMPANY',
@@ -38,7 +38,7 @@ registerEnumType(GallerySpace, {
38
38
  @Index('ix_gallery_post_1', (one: GalleryPost) => [one.domain, one.event])
39
39
  @Index('ix_gallery_post_2', (one: GalleryPost) => [one.domain, one.creator])
40
40
  @ObjectType({
41
- description: 'A post of the gallery - one or more photos, a short caption and tags, seen by everyone of the domain.'
41
+ description: "A post of the gallery - one or more photos, a short caption and tags; the company's seen by everyone of the domain."
42
42
  })
43
43
  export class GalleryPost {
44
44
  @PrimaryGeneratedColumn('uuid')
@@ -56,6 +56,11 @@ export class GalleryPost {
56
56
  @Field(type => GallerySpace, { description: "The company's gallery or the person's own." })
57
57
  space: GallerySpace
58
58
 
59
+ /* A post of the own gallery is the person's alone until they share it (2026-10-31, the user) - private by default. */
60
+ @Column({ default: false })
61
+ @Field({ description: "Whether a post of the person's own gallery is open to everyone of the domain; the company's always is." })
62
+ shared: boolean
63
+
59
64
  @Column({ type: 'text', nullable: true })
60
65
  @Field({ nullable: true, description: 'A short caption, plain text.' })
61
66
  caption?: string
@@ -152,19 +152,40 @@ async function refusal(promise: Promise<unknown>): Promise<string | undefined> {
152
152
  }
153
153
 
154
154
  describe('who sees a post', () => {
155
- it('★ everyone of the domain sees every post, the newest first', async () => {
155
+ it('★ everyone of the domain sees every shared post, the newest first', async () => {
156
156
  await post('kim', { caption: '첫 사진' })
157
- await post('lee', { caption: '내 고양이', space: 'PERSONAL' })
157
+ await post('lee', { caption: '내 고양이', space: 'PERSONAL', shared: true })
158
158
  expect(await listed('mod')).toEqual(['내 고양이', '첫 사진'])
159
159
  expect(await listed('kim', { space: 'COMPANY' })).toEqual(['첫 사진'])
160
160
  expect(await listed('kim', { authorId: people.lee.id })).toEqual(['내 고양이'])
161
+ /* The own gallery is the asker's own posts, whoever else posted (2026-10-31). */
162
+ expect(await listed('kim', { mine: true })).toEqual(['첫 사진'])
163
+ expect(await listed('lee', { mine: true })).toEqual(['내 고양이'])
164
+ expect(await listed('mod', { mine: true })).toEqual([])
161
165
  const [first] = (await doors.galleryPosts({}, 0, 1, as('mod'))).items
162
166
  expect((await doors.creator(first))?.name).toBe('lee')
163
167
  })
164
168
 
169
+ it('★ an own-gallery post is for its owner alone until shared - its photo too - and a shared screen shows only what all see', async () => {
170
+ await post('kim', { caption: '회사 사진' })
171
+ const mine = await post('lee', { caption: '내 강아지', space: 'PERSONAL' })
172
+ expect(mine.shared).toBe(false)
173
+ expect(await listed('kim')).toEqual(['회사 사진'])
174
+ expect(await listed('mod', {}, { moderator: true })).toEqual(['회사 사진'])
175
+ expect(await listed('kim', { authorId: people.lee.id })).toEqual([])
176
+ expect(await doors.galleryPost(mine.id, as('kim'))).toBeUndefined()
177
+ expect(await listed('lee')).toEqual(['내 강아지', '회사 사진'])
178
+ /* The screen saver asks for what everyone sees - never the device's person's unshared photos (decision 9). */
179
+ expect(await listed('lee', { everyone: true })).toEqual(['회사 사진'])
180
+ await doors.updateGalleryPost(mine.id, { space: 'PERSONAL', shared: true, caption: '내 강아지', tags: [] }, undefined, undefined, as('lee'))
181
+ expect(await listed('kim')).toEqual(['내 강아지', '회사 사진'])
182
+ expect(await listed('lee', { everyone: true })).toEqual(['내 강아지', '회사 사진'])
183
+ expect((await doors.galleryPost(mine.id, as('kim')))?.caption).toBe('내 강아지')
184
+ })
185
+
165
186
  it('★ the own gallery of someone who left goes from view; their company posts stay', async () => {
166
187
  await post('left', { caption: '회사 사진' })
167
- await post('left', { caption: '내 사진', space: 'PERSONAL' })
188
+ await post('left', { caption: '내 사진', space: 'PERSONAL', shared: true })
168
189
  await dataSource.getRepository(authBase.User).update(people.left.id, { status: 'inactive' })
169
190
  expect(await listed('kim')).toEqual(['회사 사진'])
170
191
  })
@@ -81,9 +81,15 @@ describe('what a post may hold', () => {
81
81
  describe('who sees a post', () => {
82
82
  const viewer = (userId: string, moderator = false) => ({ userId, moderator, activePeople: new Set(['me', 'kim']) })
83
83
 
84
- it('everyone of the domain sees a company post and the own gallery of someone here', () => {
84
+ it('everyone of the domain sees a company post and what someone here shared of their own gallery', () => {
85
85
  expect(seesPost({ space: 'COMPANY', creatorId: 'kim' }, viewer('me'))).toBe(true)
86
- expect(seesPost({ space: 'PERSONAL', creatorId: 'kim' }, viewer('me'))).toBe(true)
86
+ expect(seesPost({ space: 'PERSONAL', shared: true, creatorId: 'kim' }, viewer('me'))).toBe(true)
87
+ })
88
+
89
+ it('an unshared post of the own gallery is for its owner alone - not even a moderator sees it', () => {
90
+ expect(seesPost({ space: 'PERSONAL', creatorId: 'kim' }, viewer('kim'))).toBe(true)
91
+ expect(seesPost({ space: 'PERSONAL', creatorId: 'kim' }, viewer('me'))).toBe(false)
92
+ expect(seesPost({ space: 'PERSONAL', shared: false, creatorId: 'kim' }, viewer('me', true))).toBe(false)
87
93
  })
88
94
 
89
95
  it('a hidden post is seen by its owner and the moderators only', () => {
@@ -94,8 +100,8 @@ describe('who sees a post', () => {
94
100
  })
95
101
 
96
102
  it('the own gallery of someone who left is no longer shown; their company posts stay', () => {
97
- expect(seesPost({ space: 'PERSONAL', creatorId: 'left' }, viewer('me'))).toBe(false)
98
- expect(seesPost({ space: 'PERSONAL', creatorId: 'left' }, viewer('me', true))).toBe(false)
103
+ expect(seesPost({ space: 'PERSONAL', shared: true, creatorId: 'left' }, viewer('me'))).toBe(false)
104
+ expect(seesPost({ space: 'PERSONAL', shared: true, creatorId: 'left' }, viewer('me', true))).toBe(false)
99
105
  expect(seesPost({ space: 'COMPANY', creatorId: 'left' }, viewer('me'))).toBe(true)
100
106
  })
101
107
  })
@@ -43,7 +43,6 @@
43
43
  "gallery.text.caption-placeholder": "Add a line or two (optional)",
44
44
  "gallery.text.tag-placeholder": "Type like #workshop and press Enter",
45
45
  "gallery.text.no-event": "No event",
46
- "gallery.text.personal-note": "Everyone in the workplace sees your own gallery too.",
47
46
  "gallery.text.photos-full": "A post holds up to {count} photos.",
48
47
  "gallery.text.photos-skipped": "{count} left out - not photos, or no room.",
49
48
  "gallery.text.posted": "Posted.",
@@ -88,5 +87,19 @@
88
87
  "gallery.refusal.reason-too-long": "The reason is at most 200 characters.",
89
88
  "task.gallery.moderation.name": "Gallery moderation",
90
89
  "task.gallery.moderation.description": "Hides posts or shows them again, tidies the tags, and makes events from the gallery.",
91
- "gallery.text.tag-too-long-typed": "A tag takes up to {max} characters; this one has {length}. Shorten it and add it again."
90
+ "gallery.text.tag-too-long-typed": "A tag takes up to {max} characters; this one has {length}. Shorten it and add it again.",
91
+ "gallery.button.large": "View large",
92
+ "gallery.button.slideshow": "Slideshow",
93
+ "gallery.button.play": "Play",
94
+ "gallery.button.pause": "Pause",
95
+ "gallery.title.saver": "Gallery screen saver",
96
+ "gallery.text.saver-off": "Off",
97
+ "gallery.text.saver-after": "After {minutes} minutes idle",
98
+ "gallery.text.saver-note": "Applies to this device only. Shows company photos and shared personal photos in random order and closes when the mouse or keyboard is used.",
99
+ "gallery.text.show-empty": "No photos to show.",
100
+ "gallery.field.shared": "Who sees it",
101
+ "gallery.text.only-me": "Only me",
102
+ "gallery.text.everyone": "Everyone here",
103
+ "gallery.text.only-me-note": "Only you see it - not the moderators, and not the screen saver.",
104
+ "gallery.text.shared-note": "Everyone in the workplace sees it, and the screen saver shows it."
92
105
  }
@@ -43,7 +43,6 @@
43
43
  "gallery.text.caption-placeholder": "Añade una o dos líneas (opcional)",
44
44
  "gallery.text.tag-placeholder": "Escribe como #taller y pulsa Enter",
45
45
  "gallery.text.no-event": "Sin evento",
46
- "gallery.text.personal-note": "Todos en el lugar de trabajo ven también tu galería.",
47
46
  "gallery.text.photos-full": "Una publicación admite hasta {count} fotos.",
48
47
  "gallery.text.photos-skipped": "Se omitieron {count}: no son fotos o no caben.",
49
48
  "gallery.text.posted": "Publicado.",
@@ -88,5 +87,19 @@
88
87
  "gallery.refusal.reason-too-long": "El motivo admite hasta 200 caracteres.",
89
88
  "task.gallery.moderation.name": "Moderación de la galería",
90
89
  "task.gallery.moderation.description": "Oculta publicaciones o las vuelve a mostrar, ordena las etiquetas y crea eventos desde la galería.",
91
- "gallery.text.tag-too-long-typed": "Una etiqueta admite hasta {max} caracteres; esta tiene {length}. Acórtela y añádala de nuevo."
90
+ "gallery.text.tag-too-long-typed": "Una etiqueta admite hasta {max} caracteres; esta tiene {length}. Acórtela y añádala de nuevo.",
91
+ "gallery.button.large": "Ver en grande",
92
+ "gallery.button.slideshow": "Presentación",
93
+ "gallery.button.play": "Reproducir",
94
+ "gallery.button.pause": "Pausa",
95
+ "gallery.title.saver": "Protector de pantalla de la galería",
96
+ "gallery.text.saver-off": "Desactivado",
97
+ "gallery.text.saver-after": "Tras {minutes} minutos sin actividad",
98
+ "gallery.text.saver-note": "Solo en este dispositivo. Muestra fotos de la empresa y fotos personales compartidas en orden aleatorio y se cierra al usar el ratón o el teclado.",
99
+ "gallery.text.show-empty": "No hay fotos para mostrar.",
100
+ "gallery.field.shared": "Quién lo ve",
101
+ "gallery.text.only-me": "Solo yo",
102
+ "gallery.text.everyone": "Todos aquí",
103
+ "gallery.text.only-me-note": "Solo tú la ves: ni los moderadores ni el protector de pantalla.",
104
+ "gallery.text.shared-note": "Todos en el lugar de trabajo la ven, y el protector de pantalla la muestra."
92
105
  }
@@ -43,7 +43,6 @@
43
43
  "gallery.text.caption-placeholder": "ひとことどうぞ(任意)",
44
44
  "gallery.text.tag-placeholder": "#ワークショップ のように入力して Enter",
45
45
  "gallery.text.no-event": "イベントなし",
46
- "gallery.text.personal-note": "マイギャラリーの写真も職場の全員が見られます。",
47
46
  "gallery.text.photos-full": "1件の投稿に写真は{count}枚までです。",
48
47
  "gallery.text.photos-skipped": "写真でないか上限のため{count}件を除きました。",
49
48
  "gallery.text.posted": "投稿しました。",
@@ -88,5 +87,19 @@
88
87
  "gallery.refusal.reason-too-long": "理由は200文字までです。",
89
88
  "task.gallery.moderation.name": "ギャラリー管理",
90
89
  "task.gallery.moderation.description": "投稿の非表示・再表示、タグの整理、ギャラリーからのイベント作成。",
91
- "gallery.text.tag-too-long-typed": "タグは{max}文字までです。今は{length}文字です。短くしてもう一度入れてください。"
90
+ "gallery.text.tag-too-long-typed": "タグは{max}文字までです。今は{length}文字です。短くしてもう一度入れてください。",
91
+ "gallery.button.large": "大きく表示",
92
+ "gallery.button.slideshow": "スライドショー",
93
+ "gallery.button.play": "再生",
94
+ "gallery.button.pause": "一時停止",
95
+ "gallery.title.saver": "ギャラリーのスクリーンセーバー",
96
+ "gallery.text.saver-off": "使わない",
97
+ "gallery.text.saver-after": "{minutes}分間操作がないとき",
98
+ "gallery.text.saver-note": "この端末にだけ適用されます。会社の写真と公開された個人の写真をランダムに表示し、マウスやキーボードを使うと閉じます。",
99
+ "gallery.text.show-empty": "表示する写真がありません。",
100
+ "gallery.field.shared": "見る人",
101
+ "gallery.text.only-me": "自分だけ",
102
+ "gallery.text.everyone": "事業所の全員",
103
+ "gallery.text.only-me-note": "自分だけが見ます。管理者にも見えず、スクリーンセーバーにも出ません。",
104
+ "gallery.text.shared-note": "事業所の全員が見て、スクリーンセーバーにも出ます。"
92
105
  }
@@ -43,7 +43,6 @@
43
43
  "gallery.text.caption-placeholder": "한두 줄 남겨 보세요 (없어도 됩니다)",
44
44
  "gallery.text.tag-placeholder": "#워크숍 처럼 넣고 Enter",
45
45
  "gallery.text.no-event": "행사 없음",
46
- "gallery.text.personal-note": "내 갤러리 사진도 사업장 사람 모두가 봅니다.",
47
46
  "gallery.text.photos-full": "한 게시물에 사진은 {count}장까지입니다.",
48
47
  "gallery.text.photos-skipped": "사진이 아니거나 자리가 없어 {count}개를 빼었습니다.",
49
48
  "gallery.text.posted": "올렸습니다.",
@@ -88,5 +87,19 @@
88
87
  "gallery.refusal.reason-too-long": "까닭은 200자까지입니다.",
89
88
  "task.gallery.moderation.name": "갤러리 관리",
90
89
  "task.gallery.moderation.description": "게시물을 숨기거나 다시 보이고, 태그를 정리하고, 갤러리에서 행사를 만든다.",
91
- "gallery.text.tag-too-long-typed": "태그는 {max}자까지입니다. 지금 {length}자입니다. 줄여서 다시 넣어 주세요."
90
+ "gallery.text.tag-too-long-typed": "태그는 {max}자까지입니다. 지금 {length}자입니다. 줄여서 다시 넣어 주세요.",
91
+ "gallery.button.large": "크게 보기",
92
+ "gallery.button.slideshow": "슬라이드쇼",
93
+ "gallery.button.play": "재생",
94
+ "gallery.button.pause": "일시 정지",
95
+ "gallery.title.saver": "갤러리 화면보호기",
96
+ "gallery.text.saver-off": "사용 안 함",
97
+ "gallery.text.saver-after": "{minutes}분 동안 입력이 없으면",
98
+ "gallery.text.saver-note": "이 기기에만 적용됩니다. 회사 사진과 공개한 개인 사진을 무작위 순서로 보여 주고, 마우스나 키보드를 움직이면 닫힙니다.",
99
+ "gallery.text.show-empty": "보여 줄 사진이 없습니다.",
100
+ "gallery.field.shared": "보는 사람",
101
+ "gallery.text.only-me": "나만 보기",
102
+ "gallery.text.everyone": "사업장 모두",
103
+ "gallery.text.only-me-note": "나만 봅니다. 관리자도 보지 못하고, 화면보호기에도 나오지 않습니다.",
104
+ "gallery.text.shared-note": "사업장 사람 모두가 보고, 화면보호기에도 나옵니다."
92
105
  }
@@ -43,7 +43,6 @@
43
43
  "gallery.text.caption-placeholder": "Tulis satu dua baris (pilihan)",
44
44
  "gallery.text.tag-placeholder": "Taip seperti #bengkel dan tekan Enter",
45
45
  "gallery.text.no-event": "Tiada acara",
46
- "gallery.text.personal-note": "Semua orang di tempat kerja juga melihat galeri anda.",
47
46
  "gallery.text.photos-full": "Satu siaran memuatkan sehingga {count} foto.",
48
47
  "gallery.text.photos-skipped": "{count} ditinggalkan - bukan foto atau tiada ruang.",
49
48
  "gallery.text.posted": "Telah disiarkan.",
@@ -88,5 +87,19 @@
88
87
  "gallery.refusal.reason-too-long": "Sebab maksimum 200 aksara.",
89
88
  "task.gallery.moderation.name": "Moderasi galeri",
90
89
  "task.gallery.moderation.description": "Menyembunyikan atau menunjukkan semula siaran, mengemas tag dan mencipta acara dari galeri.",
91
- "gallery.text.tag-too-long-typed": "Tag boleh sehingga {max} aksara; tag ini ada {length}. Pendekkan dan tambah semula."
90
+ "gallery.text.tag-too-long-typed": "Tag boleh sehingga {max} aksara; tag ini ada {length}. Pendekkan dan tambah semula.",
91
+ "gallery.button.large": "Lihat besar",
92
+ "gallery.button.slideshow": "Tayangan slaid",
93
+ "gallery.button.play": "Main",
94
+ "gallery.button.pause": "Jeda",
95
+ "gallery.title.saver": "Penyelamat skrin galeri",
96
+ "gallery.text.saver-off": "Mati",
97
+ "gallery.text.saver-after": "Selepas {minutes} minit tanpa aktiviti",
98
+ "gallery.text.saver-note": "Untuk peranti ini sahaja. Memaparkan foto syarikat dan foto peribadi yang dikongsi secara rawak dan tertutup apabila tetikus atau papan kekunci digunakan.",
99
+ "gallery.text.show-empty": "Tiada foto untuk dipaparkan.",
100
+ "gallery.field.shared": "Siapa yang melihat",
101
+ "gallery.text.only-me": "Saya sahaja",
102
+ "gallery.text.everyone": "Semua di sini",
103
+ "gallery.text.only-me-note": "Hanya anda yang melihatnya - bukan moderator, dan tidak dalam penyelamat skrin.",
104
+ "gallery.text.shared-note": "Semua di tempat kerja melihatnya, dan penyelamat skrin memaparkannya."
92
105
  }
@@ -43,7 +43,6 @@
43
43
  "gallery.text.caption-placeholder": "写一两句(可不填)",
44
44
  "gallery.text.tag-placeholder": "输入如 #团建 后按 Enter",
45
45
  "gallery.text.no-event": "无活动",
46
- "gallery.text.personal-note": "我的相册中的照片同样对单位所有人可见。",
47
46
  "gallery.text.photos-full": "每个帖子最多 {count} 张照片。",
48
47
  "gallery.text.photos-skipped": "已略过 {count} 个(非照片或超出上限)。",
49
48
  "gallery.text.posted": "已上传。",
@@ -88,5 +87,19 @@
88
87
  "gallery.refusal.reason-too-long": "原因最多 200 字。",
89
88
  "task.gallery.moderation.name": "相册管理",
90
89
  "task.gallery.moderation.description": "隐藏或重新显示帖子、整理标签、在相册中创建活动。",
91
- "gallery.text.tag-too-long-typed": "标签最多 {max} 个字符,现在是 {length} 个。请缩短后重新添加。"
90
+ "gallery.text.tag-too-long-typed": "标签最多 {max} 个字符,现在是 {length} 个。请缩短后重新添加。",
91
+ "gallery.button.large": "放大查看",
92
+ "gallery.button.slideshow": "幻灯片",
93
+ "gallery.button.play": "播放",
94
+ "gallery.button.pause": "暂停",
95
+ "gallery.title.saver": "相册屏幕保护",
96
+ "gallery.text.saver-off": "关闭",
97
+ "gallery.text.saver-after": "{minutes}分钟无操作后",
98
+ "gallery.text.saver-note": "仅适用于此设备。随机播放公司照片和公开的个人照片,移动鼠标或按键即关闭。",
99
+ "gallery.text.show-empty": "没有可显示的照片。",
100
+ "gallery.field.shared": "可见范围",
101
+ "gallery.text.only-me": "仅自己",
102
+ "gallery.text.everyone": "所有同事",
103
+ "gallery.text.only-me-note": "只有你能看到,管理员看不到,屏幕保护也不显示。",
104
+ "gallery.text.shared-note": "所有同事都能看到,屏幕保护也会显示。"
92
105
  }