nodebb-plugin-admin-chats 3.0.0 → 3.1.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/client.js CHANGED
@@ -1,35 +1,45 @@
1
1
  $(document).ready(function() {
2
+ console.log(`[admin-chats] Client script loaded`);
3
+
4
+ // Initialize translations object if not already set
5
+ if (!window.adminChatsTranslations) {
6
+ window.adminChatsTranslations = {};
7
+ }
8
+
9
+ console.log(`[admin-chats] ajaxify.data:`, ajaxify.data);
10
+ console.log(`[admin-chats] window.adminChatsTranslations:`, window.adminChatsTranslations);
11
+ console.log(`[admin-chats] window.adminChatsLanguage:`, window.adminChatsLanguage);
12
+
13
+ // Get translations from ajaxify.data if available
14
+ if (ajaxify.data && ajaxify.data.adminChatsTranslations) {
15
+ window.adminChatsTranslations = ajaxify.data.adminChatsTranslations;
16
+ window.adminChatsLanguage = ajaxify.data.adminChatsLanguage;
17
+ window.adminChatsAccess = ajaxify.data.adminChatsAccess;
18
+ window.adminChatsIsAdmin = ajaxify.data.adminChatsIsAdmin;
19
+ window.adminChatsCanManage = ajaxify.data.adminChatsCanManage;
20
+ console.log(`[admin-chats] Loaded translations from ajaxify.data`);
21
+ }
22
+
2
23
  syncAdminAccessFromAjaxify();
3
24
  fetchAdminPrivileges();
4
25
  const LOCK_PREFIX = '[admin-chat-lock]';
5
26
  const roomStateCache = new Map();
6
- const TEXT = {
7
- he: {
8
- lockBanner: '🔒 חדר זה ננעל ע"י המנהלים.',
9
- menuLock: 'נעל חדר',
10
- menuRelease: 'שחרר נעילה',
11
- updateError: 'לא ניתן לעדכן את נעילת החדר.',
12
- viewChats: "צפיה בצ'אטים",
13
- emptyState: "אנא בחר צ'אט מסרגל הצד.",
14
- participants: 'משתתפים',
15
- },
16
- en: {
17
- lockBanner: '🔒 This room was locked by the administrators.',
18
- menuLock: 'Lock Room',
19
- menuRelease: 'Release Lock',
20
- updateError: 'Unable to update room lock.',
21
- viewChats: 'View Chats',
22
- emptyState: 'Please select a chat from the sidebar.',
23
- participants: 'Participants',
24
- },
25
- };
26
27
 
27
- function isEnglishSystem() {
28
- return $('html').attr('lang') && $('html').attr('lang').startsWith('en');
29
- }
30
28
  function t(key) {
31
- const dict = isEnglishSystem() ? TEXT.en : TEXT.he;
32
- return dict[key];
29
+ // Get translation from server (from language files)
30
+ if (window.adminChatsTranslations && window.adminChatsTranslations[key]) {
31
+ return window.adminChatsTranslations[key];
32
+ }
33
+ // If translation not found, log warning and return the key itself
34
+ console.warn(`[admin-chats] Translation not found for key: ${key}`);
35
+ console.log(`[admin-chats] Available translations:`, window.adminChatsTranslations);
36
+ return key;
37
+ }
38
+
39
+ function isEnglishSystem() {
40
+ const lang = window.adminChatsLanguage || $('html').attr('lang') || 'en';
41
+ const langCode = lang.split('-')[0];
42
+ return langCode === 'en';
33
43
  }
34
44
 
35
45
  function hasAdminChatsAccess() {
@@ -129,8 +139,8 @@ $(document).ready(function() {
129
139
 
130
140
  // Get lockedAction message from server translations only (based on forum settings)
131
141
  function getLockedActionMessage() {
132
- if (window.adminChatsTranslations && window.adminChatsTranslations.lockedAction) {
133
- return window.adminChatsTranslations.lockedAction;
142
+ if (window.adminChatsTranslations && window.adminChatsTranslations['errors.lockedAction']) {
143
+ return window.adminChatsTranslations['errors.lockedAction'];
134
144
  }
135
145
  // If server translation not available, default to English
136
146
  return 'This action cannot be performed in a locked room.';
@@ -281,7 +291,7 @@ $(document).ready(function() {
281
291
  currentText.includes('You have no active chats') ||
282
292
  currentText === 'You have no active chats.'
283
293
  ) {
284
- $(this).text(t('emptyState'));
294
+ $(this).text(t('empty.selectChat'));
285
295
  $(this).removeClass('text-muted');
286
296
  }
287
297
  });
@@ -416,7 +426,7 @@ $(document).ready(function() {
416
426
  }
417
427
 
418
428
  const positionStyle = isEnglishSystem() ? 'float:right; clear:both;' : 'float:left; clear:both;';
419
- target.prepend(`<div class="admin-chat-lock-banner alert alert-warning mb-2 text-start" style="${positionStyle} max-width: fit-content;">${t('lockBanner')}</div>`);
429
+ target.prepend(`<div class="admin-chat-lock-banner alert alert-warning mb-2 text-start" style="${positionStyle} max-width: fit-content;">${t('lock.banner')}</div>`);
420
430
  }
421
431
 
422
432
  function updateLockedActionVisibility($window, hidden) {
@@ -457,7 +467,7 @@ $(document).ready(function() {
457
467
  if (!text.includes(LOCK_PREFIX.slice(1, -1)) && !text.includes('admin-chat-lock')) {
458
468
  return;
459
469
  }
460
- $el.text(t('lockBanner'));
470
+ $el.text(t('lock.banner'));
461
471
  });
462
472
  }
463
473
 
@@ -483,7 +493,7 @@ $(document).ready(function() {
483
493
 
484
494
  const lockData = roomData.adminChatLock || {};
485
495
  const isLocked = !!lockData.isLocked;
486
- const itemText = isLocked ? t('menuRelease') : t('menuLock');
496
+ const itemText = isLocked ? t('menu.release') : t('menu.lock');
487
497
  const iconClass = isLocked ? 'fa-lock-open' : 'fa-lock';
488
498
  const menuItemHtml = `
489
499
  <li role="presentation" class="admin-chat-lock-item-wrap">
@@ -588,6 +598,64 @@ $(document).ready(function() {
588
598
  return await response.json();
589
599
  }
590
600
 
601
+ async function injectProfileChatsMenu() {
602
+ const allowed = await resolveAdminChatsAccess();
603
+ if (!allowed) {
604
+ return;
605
+ }
606
+
607
+ const isUserProfile = $(".account").length && window.location.pathname.includes("/user/");
608
+ if (!isUserProfile) {
609
+ return;
610
+ }
611
+
612
+ const userSlug = (ajaxify && ajaxify.data && (ajaxify.data.userslug || (ajaxify.data.user && ajaxify.data.user.userslug))) || null;
613
+ if (!userSlug) {
614
+ return;
615
+ }
616
+
617
+ const relativePath = config.relative_path || "";
618
+ const menuItemHtml = `
619
+ <li role="presentation" class="admin-chats-profile-link">
620
+ <a class="dropdown-item rounded-1 d-flex align-items-center gap-2" href="${relativePath}/user/${userSlug}/chats" role="menuitem">
621
+ <i class="far fa-fw fa-comments"></i>
622
+ <span>${t("profile.viewChats")}</span>
623
+ </a>
624
+ </li>
625
+ `;
626
+
627
+ let menu = $(".account-sub-links");
628
+ if (!menu.length) {
629
+ const container = $(".account .flex-shrink-0.d-flex.gap-1").first();
630
+ const fallbackContainer = container.length ? container : $(".account .flex-shrink-0").first();
631
+
632
+ if (fallbackContainer.length) {
633
+ const menuHtml = `
634
+ <div class="btn-group bottom-sheet admin-chats-privileges-menu">
635
+ <button type="button" class="btn btn-light dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
636
+ <i class="fa fa-gear fa-fw"></i>
637
+ </button>
638
+ <ul class="dropdown-menu dropdown-menu-end p-1 text-sm account-sub-links" role="menu"></ul>
639
+ </div>
640
+ `;
641
+ fallbackContainer.append(menuHtml);
642
+ menu = fallbackContainer.find(".account-sub-links").last();
643
+ }
644
+ }
645
+
646
+ if (menu.length) {
647
+ menu.find(".admin-chats-profile-link").remove();
648
+ menu.find(".admin-chats-profile-divider").remove();
649
+ menu.find(`a[href*="/user/${userSlug}/chats"]`).parent().remove();
650
+ menu.prepend(menuItemHtml);
651
+
652
+ const hasOtherItems = menu.find("li").not(".admin-chats-profile-link").length > 0;
653
+ if (hasOtherItems) {
654
+ menu.find(".admin-chats-profile-link").after('<li role="presentation" class="dropdown-divider admin-chats-profile-divider"></li>');
655
+ }
656
+ }
657
+ }
658
+
591
659
  $(window).on('action:ajaxify.end', function(ev, data) {
592
660
  syncAdminAccessFromAjaxify();
593
661
  fetchAdminPrivileges();
@@ -746,7 +814,7 @@ $(document).ready(function() {
746
814
  setCachedRoomData(roomId, cached);
747
815
  await refreshChatUi();
748
816
  } catch (err) {
749
- app.alertError(t('updateError'));
817
+ app.alertError(t('errors.update'));
750
818
  $button.removeClass('disabled').removeAttr('aria-disabled');
751
819
  }
752
820
  });
@@ -765,3 +833,4 @@ $(document).ready(function() {
765
833
  app.alertError._adminChatsWrapped = true;
766
834
  }
767
835
  });
836
+
package/library.js CHANGED
@@ -7,13 +7,86 @@ const db = require.main.require('./src/database');
7
7
  const meta = require.main.require('./src/meta');
8
8
  const privileges = require.main.require('./src/privileges');
9
9
  const helpers = require.main.require('./src/controllers/helpers');
10
+ const fs = require('fs');
11
+ const path = require('path');
10
12
 
11
13
  const plugin = {};
12
14
  const LOCK_PREFIX = '[admin-chat-lock]';
13
- const LOCKED_MESSAGE_TEXT = '🔒 חדר זה ננעל ע"י המנהלים.';
15
+ const LOCKED_MESSAGE_TEXT_KEY = 'lock.banner';
14
16
  const ADMIN_CHATS_PRIVILEGE = 'admin-chats:view';
15
17
  const ADMIN_CHATS_MANAGE_PRIVILEGE = 'admin-chats:manage';
16
18
 
19
+ // Load translations from language files
20
+ const translations = {};
21
+ function loadTranslations() {
22
+ // Try multiple possible paths
23
+ const possiblePaths = [
24
+ path.join(__dirname, 'public', 'languages'),
25
+ path.join(__dirname, '..', '..', 'public', 'languages'),
26
+ path.resolve(__dirname, 'public', 'languages'),
27
+ ];
28
+
29
+ let languagesDir = null;
30
+ for (const dir of possiblePaths) {
31
+ if (fs.existsSync(dir)) {
32
+ languagesDir = dir;
33
+ console.log(`[admin-chats] Found languages directory at: ${languagesDir}`);
34
+ break;
35
+ }
36
+ }
37
+
38
+ if (!languagesDir) {
39
+ console.warn(`[admin-chats] Languages directory not found in any of these paths:`, possiblePaths);
40
+ return;
41
+ }
42
+
43
+ try {
44
+ const langs = fs.readdirSync(languagesDir);
45
+ langs.forEach(lang => {
46
+ const langPath = path.join(languagesDir, lang, 'admin-chats.json');
47
+ try {
48
+ if (fs.existsSync(langPath)) {
49
+ const content = fs.readFileSync(langPath, 'utf8');
50
+ translations[lang] = JSON.parse(content);
51
+ console.log(`[admin-chats] Loaded translations for language: ${lang}`);
52
+ }
53
+ } catch (err) {
54
+ console.error(`[admin-chats] Error loading translations for ${lang}:`, err.message);
55
+ }
56
+ });
57
+ } catch (err) {
58
+ console.error(`[admin-chats] Error reading languages directory:`, err.message);
59
+ }
60
+ }
61
+ loadTranslations();
62
+ function getTranslationForLang(lang, key) {
63
+ if (!key) {
64
+ return '';
65
+ }
66
+ if (lang && translations[lang] && translations[lang][key]) {
67
+ return translations[lang][key];
68
+ }
69
+ const langCode = lang ? lang.split('-')[0] : '';
70
+ if (langCode && translations[langCode] && translations[langCode][key]) {
71
+ return translations[langCode][key];
72
+ }
73
+ if (translations.en && translations.en[key]) {
74
+ return translations.en[key];
75
+ }
76
+ if (translations['en-GB'] && translations['en-GB'][key]) {
77
+ return translations['en-GB'][key];
78
+ }
79
+ return '';
80
+ }
81
+
82
+ function getDefaultLockMessage() {
83
+ const defaultLang = meta.config.defaultLang || 'en-GB';
84
+ return getTranslationForLang(defaultLang, LOCKED_MESSAGE_TEXT_KEY)
85
+ || getTranslationForLang('en-GB', LOCKED_MESSAGE_TEXT_KEY)
86
+ || getTranslationForLang('en', LOCKED_MESSAGE_TEXT_KEY)
87
+ || 'This room was locked by the administrators.';
88
+ }
89
+
17
90
  async function getLockedActionMessage(uid) {
18
91
  try {
19
92
  const userData = await User.getUserData(uid);
@@ -21,15 +94,22 @@ async function getLockedActionMessage(uid) {
21
94
 
22
95
  // If user doesn't have specific language setting, use forum default
23
96
  if (!userLang) {
24
- const meta = require.main.require('./src/meta');
25
97
  userLang = meta.config.defaultLang || 'en-GB';
26
98
  }
27
99
 
28
- if (userLang === 'he' || userLang === 'he-IL') {
29
- return 'לא ניתן לבצע פעולה זו בחדר נעול';
30
- } else {
31
- return 'This action cannot be performed in a locked room.';
100
+ // Extract language code (e.g., 'he' from 'he-IL', 'fr' from 'fr-FR')
101
+ const langCode = userLang.split('-')[0];
102
+
103
+ // Try to get translation from loaded files
104
+ if (translations[langCode] && translations[langCode]['errors.lockedAction']) {
105
+ return translations[langCode]['errors.lockedAction'];
106
+ }
107
+ if (translations[userLang] && translations[userLang]['errors.lockedAction']) {
108
+ return translations[userLang]['errors.lockedAction'];
32
109
  }
110
+
111
+ // Fallback to English only
112
+ return 'This action cannot be performed in a locked room.';
33
113
  } catch (err) {
34
114
  // Default to English if we can't determine language
35
115
  return 'This action cannot be performed in a locked room.';
@@ -77,6 +157,13 @@ plugin.init = async function (params) {
77
157
  overrideMessagingFunctions();
78
158
  overrideChatsApi();
79
159
  overrideCoreChatRedirect(params.controllers);
160
+
161
+ // Add middleware to inject translations into every page
162
+ params.app.use((req, res, next) => {
163
+ // Store translations in res.locals so they can be accessed in templates
164
+ res.locals.adminChatsTranslations = translations;
165
+ next();
166
+ });
80
167
  };
81
168
  plugin.registerPrivileges = async function (data) {
82
169
  if (!data || !data.privileges || typeof data.privileges.set !== 'function') {
@@ -153,43 +240,126 @@ plugin.filterUserNotificationsGetNotifications = async function (data) {
153
240
 
154
241
  return data;
155
242
  };
156
- plugin.addScripts = async function (data) {
157
- let isHebrew = false;
158
-
159
- const uid = data.req && data.req.uid;
243
+ async function buildAdminChatsClientContext(req) {
244
+ console.log(`[admin-chats] addScripts hook called`);
245
+ const uid = req && req.uid;
246
+ console.log(`[admin-chats] addScripts called with uid: ${uid}`);
247
+
248
+ let userLang = 'en-GB';
249
+
160
250
  if (uid) {
161
251
  try {
162
252
  const userData = await User.getUserData(uid);
163
- let userLang = userData && userData.settings && userData.userLang;
164
-
253
+ console.log(`[admin-chats] Full userData:`, userData);
254
+ console.log(`[admin-chats] User data for uid ${uid}:`, {
255
+ userLang: userData && userData.userLang,
256
+ settingsUserLang: userData && userData.settings && userData.settings.userLang,
257
+ });
258
+
259
+ // Try different ways to get user language
260
+ userLang = userData && userData.userLang;
261
+ if (!userLang && userData && userData.settings) {
262
+ userLang = userData.settings.userLang;
263
+ }
264
+
165
265
  // If user doesn't have specific language setting, use forum default
166
266
  if (!userLang) {
167
- const meta = require.main.require('./src/meta');
168
267
  userLang = meta.config.defaultLang || 'en-GB';
268
+ console.log(`[admin-chats] Using forum default language: ${userLang}`);
169
269
  }
170
-
171
- isHebrew = userLang === 'he' || userLang === 'he-IL';
172
270
  } catch (err) {
173
- // Default to English if error
174
- isHebrew = false;
271
+ console.error(`[admin-chats] Error getting user language:`, err.message);
272
+ userLang = meta.config.defaultLang || 'en-GB';
175
273
  }
274
+ } else {
275
+ // No uid, use forum default language
276
+ userLang = meta.config.defaultLang || 'en-GB';
277
+ console.log(`[admin-chats] No uid, using forum default language: ${userLang}`);
176
278
  }
177
-
178
- // Only send the lockedAction translation from server
179
- const translations = {
180
- lockedAction: isHebrew ? 'לא ניתן לבצע פעולה זו בחדר נעול' : 'This action cannot be performed in a locked room.',
181
- };
279
+
280
+ // Build translations based on user language from loaded files
281
+ let clientTranslations = {};
282
+ const langCode = userLang.split('-')[0];
283
+
284
+ console.log(`[admin-chats] Forum default language: ${meta.config.defaultLang}`);
285
+ console.log(`[admin-chats] User language: ${userLang}, Language code: ${langCode}`);
286
+ console.log(`[admin-chats] Available translations:`, Object.keys(translations));
287
+
288
+ // Try to get translations from loaded files
289
+ // First try exact match (e.g., 'he-IL')
290
+ if (translations[userLang]) {
291
+ clientTranslations = translations[userLang];
292
+ console.log(`[admin-chats] Using exact match: ${userLang}`);
293
+ }
294
+ // Then try language code (e.g., 'he' from 'he-IL')
295
+ else if (translations[langCode]) {
296
+ clientTranslations = translations[langCode];
297
+ console.log(`[admin-chats] Using language code: ${langCode}`);
298
+ }
299
+ // Then try full locale (e.g., 'en-GB')
300
+ else if (translations[userLang.replace('-', '-')]) {
301
+ clientTranslations = translations[userLang];
302
+ console.log(`[admin-chats] Using full locale: ${userLang}`);
303
+ }
304
+ // Fallback to English
305
+ else {
306
+ clientTranslations = translations['en'] || translations['en-GB'] || {};
307
+ console.log(`[admin-chats] Using fallback (en or en-GB)`);
308
+ }
309
+
310
+ console.log(`[admin-chats] Sending translations:`, Object.keys(clientTranslations));
311
+ console.log(`[admin-chats] clientTranslations content:`, clientTranslations);
182
312
 
183
313
  const isAdmin = uid ? await User.isAdministrator(uid) : false;
184
314
  const adminChatsManage = uid ? (isAdmin || await privileges.global.can(ADMIN_CHATS_MANAGE_PRIVILEGE, uid)) : false;
185
315
  const adminChatsAccess = uid ? (isAdmin || adminChatsManage || await privileges.global.can(ADMIN_CHATS_PRIVILEGE, uid)) : false;
186
316
 
317
+ return {
318
+ clientTranslations,
319
+ userLang,
320
+ adminChatsAccess,
321
+ adminChatsIsAdmin: isAdmin,
322
+ adminChatsCanManage: adminChatsManage,
323
+ };
324
+ }
325
+
326
+ plugin.addTemplateData = async function (data) {
327
+ const templateData = data && data.templateData ? data.templateData : null;
328
+ if (!templateData) {
329
+ return data;
330
+ }
331
+
332
+ const context = await buildAdminChatsClientContext(data.req);
333
+ templateData.adminChatsTranslations = context.clientTranslations;
334
+ templateData.adminChatsLanguage = context.userLang;
335
+ templateData.adminChatsAccess = context.adminChatsAccess;
336
+ templateData.adminChatsIsAdmin = context.adminChatsIsAdmin;
337
+ templateData.adminChatsCanManage = context.adminChatsCanManage;
338
+
339
+ console.log(`[admin-chats] Added translations to template data`);
340
+ return data;
341
+ };
342
+
343
+ plugin.addScripts = async function (data) {
344
+ const context = await buildAdminChatsClientContext(data.req);
345
+
346
+ // Add translations directly to data object
347
+ data.adminChatsTranslations = context.clientTranslations;
348
+ data.adminChatsLanguage = context.userLang;
349
+ data.adminChatsAccess = context.adminChatsAccess;
350
+ data.adminChatsIsAdmin = context.adminChatsIsAdmin;
351
+ data.adminChatsCanManage = context.adminChatsCanManage;
352
+
353
+ // Also add as inline script to ensure it's available before client.js runs
187
354
  data.scripts = data.scripts || [];
355
+ const scriptContent = `window.adminChatsTranslations = ${JSON.stringify(context.clientTranslations)}; window.adminChatsLanguage = '${context.userLang}'; window.adminChatsAccess = ${context.adminChatsAccess}; window.adminChatsIsAdmin = ${context.adminChatsIsAdmin}; window.adminChatsCanManage = ${context.adminChatsCanManage};`;
188
356
  data.scripts.push({
189
357
  src: false,
190
- script: `window.adminChatsTranslations = ${JSON.stringify(translations)}; window.adminChatsAccess = ${adminChatsAccess}; window.adminChatsIsAdmin = ${isAdmin}; window.adminChatsCanManage = ${adminChatsManage};`
358
+ script: scriptContent
191
359
  });
192
360
 
361
+ console.log(`[admin-chats] Added translations to data object and scripts`);
362
+
193
363
  return data;
194
364
  };
195
365
 
@@ -1003,7 +1173,7 @@ function buildRoomMessagesWithLockNotice(messages, lockData) {
1003
1173
  }
1004
1174
 
1005
1175
  cleanedMessages.push({
1006
- content: `${LOCK_PREFIX} ${LOCKED_MESSAGE_TEXT}`,
1176
+ content: `${LOCK_PREFIX} ${getDefaultLockMessage()}`,
1007
1177
  fromuid: lockData.lockedBy,
1008
1178
  uid: lockData.lockedBy,
1009
1179
  timestamp: lockData.lockedAt,
@@ -1029,7 +1199,7 @@ function stripAdminLockMessages(messages) {
1029
1199
  function isAdminLockMessage(msg) {
1030
1200
  const content = String(msg && msg.content || '');
1031
1201
  return content.includes('admin-chat-lock') ||
1032
- content.includes(LOCKED_MESSAGE_TEXT) ||
1202
+ content.includes(getDefaultLockMessage()) ||
1033
1203
  content.includes('נפתח מחדש ע"י המנהלים') ||
1034
1204
  content.includes('modules:chat.system.[admin-chat-lock]');
1035
1205
  }
@@ -1070,3 +1240,11 @@ function getRoomId(payload) {
1070
1240
  }
1071
1241
 
1072
1242
  module.exports = plugin;
1243
+
1244
+
1245
+
1246
+
1247
+
1248
+
1249
+
1250
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-admin-chats",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "A plugin that allows the administrator to view and manage user chats.",
5
5
  "main": "library.js",
6
6
  "nbbpm": {
package/plugin.json CHANGED
@@ -61,8 +61,8 @@
61
61
  "method": "filterRenameRoom"
62
62
  },
63
63
  {
64
- "hook": "filter:scripts.get",
65
- "method": "addScripts"
64
+ "hook": "filter:middleware.render",
65
+ "method": "addTemplateData"
66
66
  },
67
67
  {
68
68
  "hook": "filter:notifications.create",
@@ -79,3 +79,6 @@
79
79
  ],
80
80
  "languages": "public/languages"
81
81
  }
82
+
83
+
84
+
@@ -0,0 +1,12 @@
1
+ {
2
+ "lock.banner": "🔒 This room was locked by the administrators.",
3
+ "menu.lock": "Lock Room",
4
+ "menu.release": "Release Lock",
5
+ "errors.update": "Unable to update room lock.",
6
+ "profile.viewChats": "View Chats",
7
+ "empty.selectChat": "Please select a chat from the sidebar.",
8
+ "errors.lockedAction": "This action cannot be performed in a locked room.",
9
+ "admin-chats-view": "View all chats",
10
+ "admin-chats-manage": "Manage chat rooms",
11
+ "participants": "Participants"
12
+ }
@@ -7,5 +7,6 @@
7
7
  "empty.selectChat": "Please select a chat from the sidebar.",
8
8
  "errors.lockedAction": "This action cannot be performed in a locked room.",
9
9
  "admin-chats-view": "View all chats",
10
- "admin-chats-manage": "Manage chat rooms"
10
+ "admin-chats-manage": "Manage chat rooms",
11
+ "participants": "Participants"
11
12
  }
@@ -0,0 +1,12 @@
1
+ {
2
+ "lock.banner": "🔒 Ce salon a été verrouillé par les administrateurs.",
3
+ "menu.lock": "Verrouiller le salon",
4
+ "menu.release": "Déverrouiller le salon",
5
+ "errors.update": "Impossible de mettre à jour le verrouillage du salon.",
6
+ "profile.viewChats": "Voir les Chats",
7
+ "empty.selectChat": "Veuillez sélectionner un Chat dans la barre latérale.",
8
+ "errors.lockedAction": "Cette action ne peut pas être effectuée dans un salon verrouillé.",
9
+ "admin-chats-view": "Voir tous les Chats",
10
+ "admin-chats-manage": "Gérer les salons de discussion",
11
+ "participants": "Participants"
12
+ }
@@ -7,5 +7,6 @@
7
7
  "empty.selectChat": "אנא בחר צ'אט מסרגל הצד.",
8
8
  "errors.lockedAction": "לא ניתן לבצע פעולה זו בחדר נעול",
9
9
  "admin-chats-view": "צפיה בכל הצ'אטים",
10
- "admin-chats-manage": "ניהול חדרי צ'אט"
10
+ "admin-chats-manage": "ניהול חדרי צ'אט",
11
+ "participants": "משתתפים"
11
12
  }