@brftech/filex-core 0.42.2 → 0.43.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.
Files changed (234) hide show
  1. package/README.md +14 -2
  2. package/dist/ArchiveViewer-BqU761Wk.js +149 -0
  3. package/dist/ArchiveViewer-BqU761Wk.js.map +1 -0
  4. package/dist/CsvViewer-ByHVK5ga.js +172 -0
  5. package/dist/CsvViewer-ByHVK5ga.js.map +1 -0
  6. package/dist/{DrawioViewer-CC6rJXeP.js → DrawioViewer-Bgrj8-zG.js} +45 -44
  7. package/dist/DrawioViewer-Bgrj8-zG.js.map +1 -0
  8. package/dist/{EpubViewer-CAoTSXTO.js → EpubViewer-DJwUKlHW.js} +50 -50
  9. package/dist/EpubViewer-DJwUKlHW.js.map +1 -0
  10. package/dist/IpynbViewer-Bz0eJ4NS.js +184 -0
  11. package/dist/IpynbViewer-Bz0eJ4NS.js.map +1 -0
  12. package/dist/MermaidViewer-CEDTD_pp.js +136 -0
  13. package/dist/MermaidViewer-CEDTD_pp.js.map +1 -0
  14. package/dist/PsdViewer-CZcxUpZO.js +121 -0
  15. package/dist/PsdViewer-CZcxUpZO.js.map +1 -0
  16. package/dist/TiffViewer-BwI62CbQ.js +141 -0
  17. package/dist/TiffViewer-BwI62CbQ.js.map +1 -0
  18. package/dist/{Viewer3D-CdZeK4j4.js → Viewer3D-DoeZcuMm.js} +16 -16
  19. package/dist/Viewer3D-DoeZcuMm.js.map +1 -0
  20. package/dist/filex-core.js +475 -219
  21. package/dist/filex-core.umd.cjs +165 -108
  22. package/dist/filex-core.umd.cjs.map +1 -1
  23. package/dist/fonts/caveat-latin-ext.woff2 +0 -0
  24. package/dist/fonts/caveat-latin.woff2 +0 -0
  25. package/dist/fonts/dancing-script-latin-ext.woff2 +0 -0
  26. package/dist/fonts/dancing-script-latin.woff2 +0 -0
  27. package/dist/fonts/homemade-apple-latin.woff2 +0 -0
  28. package/dist/fonts/source-serif-4-latin-ext.woff2 +0 -0
  29. package/dist/fonts/source-serif-4-latin.woff2 +0 -0
  30. package/dist/index-DuWnGuv5.js +35901 -0
  31. package/dist/index-DuWnGuv5.js.map +1 -0
  32. package/dist/index.d.ts +5227 -553
  33. package/dist/pdf-CaPYsOUo.js +21302 -0
  34. package/dist/pdf-CaPYsOUo.js.map +1 -0
  35. package/dist/style.css +1 -1
  36. package/package.json +1 -1
  37. package/src/FileExplorer.vue +1055 -253
  38. package/src/assets/fonts/LICENSE-Apache-2.0.txt +202 -0
  39. package/src/assets/fonts/LICENSE-OFL-1.1.txt +93 -0
  40. package/src/assets/fonts/README.md +58 -0
  41. package/src/assets/fonts/caveat-latin-ext.woff2 +0 -0
  42. package/src/assets/fonts/caveat-latin.woff2 +0 -0
  43. package/src/assets/fonts/dancing-script-latin-ext.woff2 +0 -0
  44. package/src/assets/fonts/dancing-script-latin.woff2 +0 -0
  45. package/src/assets/fonts/homemade-apple-latin.woff2 +0 -0
  46. package/src/assets/fonts/source-serif-4-latin-ext.woff2 +0 -0
  47. package/src/assets/fonts/source-serif-4-latin.woff2 +0 -0
  48. package/src/components/AdvancedSearch.vue +49 -0
  49. package/src/components/Breadcrumb.vue +5 -5
  50. package/src/components/ChoiceButtons.vue +152 -0
  51. package/src/components/CommandPalette.vue +5 -5
  52. package/src/components/ConnectionGuideView.vue +1 -1
  53. package/src/components/ConnectionNotice.vue +59 -0
  54. package/src/components/ConnectionsPanel.vue +30 -568
  55. package/src/components/ContextMenu.vue +31 -5
  56. package/src/components/DataTable.vue +1331 -0
  57. package/src/components/FilePane.vue +3 -0
  58. package/src/components/FilterBar.vue +12 -5
  59. package/src/components/GalleryView.vue +50 -2
  60. package/src/components/GridView.vue +51 -3
  61. package/src/components/HomeView.vue +18 -6
  62. package/src/components/InspectorPanel.vue +134 -17
  63. package/src/components/ListView.vue +366 -900
  64. package/src/components/LogoMark.vue +57 -0
  65. package/src/components/NFSExportsPanel.vue +76 -28
  66. package/src/components/OnboardingTour.vue +19 -7
  67. package/src/components/OperationsCenter.vue +77 -1
  68. package/src/components/PendingOpsTray.vue +59 -5
  69. package/src/components/PresenceBar.vue +7 -7
  70. package/src/components/ProductVersion.vue +31 -0
  71. package/src/components/QuickLook.vue +10 -14
  72. package/src/components/RecentlyOpened.vue +31 -15
  73. package/src/components/RecoveryKeyModal.vue +1 -1
  74. package/src/components/RowActions.vue +134 -0
  75. package/src/components/S3KeysPanel.vue +98 -62
  76. package/src/components/SSHKeysPanel.vue +79 -30
  77. package/src/components/SideNav.vue +260 -96
  78. package/src/components/StarButton.vue +1 -0
  79. package/src/components/StorageFields.vue +74 -6
  80. package/src/components/StorageTags.vue +62 -0
  81. package/src/components/TabBar.vue +1 -1
  82. package/src/components/TagKindIcon.vue +56 -0
  83. package/src/components/TagPicker.vue +226 -40
  84. package/src/components/ThemePalette.vue +4 -4
  85. package/src/components/TimeZonePicker.vue +29 -10
  86. package/src/components/TokensPanel.vue +82 -31
  87. package/src/components/Toolbar.vue +28 -6
  88. package/src/components/plugin/PluginConfirmModal.vue +54 -0
  89. package/src/components/plugin/PluginInspectorSection.vue +149 -0
  90. package/src/components/plugin/PluginPageView.vue +339 -0
  91. package/src/components/plugin/PluginViewModal.vue +159 -0
  92. package/src/components/plugin/SurfaceConversation.vue +76 -0
  93. package/src/components/plugin/SurfaceFooterButtons.vue +73 -0
  94. package/src/components/plugin/SurfaceRenderer.vue +299 -0
  95. package/src/components/plugin/SurfaceSections.vue +91 -0
  96. package/src/components/plugin/nodes/PdfFieldEditor.vue +509 -0
  97. package/src/components/plugin/nodes/SurfaceFileChooser.vue +94 -0
  98. package/src/components/plugin/nodes/SurfaceForm.vue +82 -0
  99. package/src/components/plugin/nodes/SurfaceList.vue +127 -0
  100. package/src/components/plugin/nodes/SurfacePdfFields.vue +1285 -0
  101. package/src/components/plugin/nodes/SurfacePeoplePicker.vue +213 -0
  102. package/src/components/plugin/nodes/SurfacePinInput.vue +57 -0
  103. package/src/components/plugin/nodes/SurfacePreview.vue +119 -0
  104. package/src/components/plugin/nodes/SurfaceProgress.vue +48 -0
  105. package/src/components/plugin/nodes/SurfaceSignaturePad.vue +472 -0
  106. package/src/components/plugin/nodes/SurfaceSteps.vue +53 -0
  107. package/src/components/public/PublicLanguagePicker.vue +58 -0
  108. package/src/components/public/PublicLinkPage.vue +286 -0
  109. package/src/components/public/PublicLinkPreview.vue +85 -0
  110. package/src/components/public/PublicPinGate.vue +108 -0
  111. package/src/components/public/PublicRequestBody.vue +204 -0
  112. package/src/components/public/PublicShareBody.vue +188 -0
  113. package/src/components/public/PublicShell.vue +332 -0
  114. package/src/composables/useConnections.ts +8 -2
  115. package/src/composables/useFileApi.ts +183 -44
  116. package/src/composables/useLocale.ts +277 -41
  117. package/src/composables/useNFSExports.ts +8 -10
  118. package/src/composables/useOperations.ts +44 -3
  119. package/src/composables/usePendingOps.ts +59 -8
  120. package/src/composables/usePluginActions.ts +121 -0
  121. package/src/composables/usePluginSurface.ts +223 -0
  122. package/src/composables/usePublicBranding.ts +172 -0
  123. package/src/composables/usePublicLink.ts +573 -0
  124. package/src/composables/usePublicPage.ts +61 -0
  125. package/src/composables/useS3Keys.ts +17 -12
  126. package/src/composables/useSSHKeys.ts +8 -10
  127. package/src/composables/useTokens.ts +8 -12
  128. package/src/composables/useUploadChunked.ts +18 -8
  129. package/src/composables/useViewerFetch.ts +6 -3
  130. package/src/index.ts +404 -8
  131. package/src/lib/actionIcons.ts +46 -1
  132. package/src/lib/advSearch.ts +14 -7
  133. package/src/lib/appLock.ts +130 -0
  134. package/src/lib/connection.ts +110 -0
  135. package/src/lib/destinationTree.ts +19 -3
  136. package/src/lib/direction.ts +353 -0
  137. package/src/lib/dragGhost.ts +6 -0
  138. package/src/lib/dropLimits.ts +69 -0
  139. package/src/lib/errorWords.ts +394 -0
  140. package/src/lib/externalReach.ts +5 -1
  141. package/src/lib/fileFilters.ts +7 -2
  142. package/src/lib/fileIcons.ts +4 -0
  143. package/src/lib/internalPaths.ts +77 -0
  144. package/src/lib/listInput.ts +64 -0
  145. package/src/lib/listing.ts +58 -23
  146. package/src/lib/modalStack.ts +26 -0
  147. package/src/lib/nodeRow.ts +18 -3
  148. package/src/lib/pdfFieldRules.ts +126 -0
  149. package/src/lib/pdfFields.ts +589 -0
  150. package/src/lib/pdfFieldsGeom.ts +203 -0
  151. package/src/lib/pdfjsLoader.ts +51 -0
  152. package/src/lib/personName.ts +47 -0
  153. package/src/lib/pluginApplies.ts +123 -0
  154. package/src/lib/pluginLabel.ts +72 -0
  155. package/src/lib/pluginMenu.ts +253 -0
  156. package/src/lib/pluginPage.ts +58 -0
  157. package/src/lib/plural.ts +181 -0
  158. package/src/lib/popupLayer.ts +103 -0
  159. package/src/lib/prefs.ts +563 -0
  160. package/src/lib/productVersion.ts +35 -0
  161. package/src/lib/publicLayout.ts +39 -0
  162. package/src/lib/serviceGate.ts +104 -0
  163. package/src/lib/shareTtl.ts +25 -11
  164. package/src/lib/signFonts.ts +85 -0
  165. package/src/lib/signaturePad.ts +111 -0
  166. package/src/lib/sortOrder.ts +100 -38
  167. package/src/lib/star.ts +7 -1
  168. package/src/lib/surfaceCell.ts +41 -0
  169. package/src/lib/surfaceConditions.ts +156 -0
  170. package/src/lib/surfaceOpen.ts +106 -0
  171. package/src/lib/surfaceSteps.ts +68 -0
  172. package/src/lib/surfaceValues.ts +191 -0
  173. package/src/lib/symlink.ts +150 -0
  174. package/src/lib/tableColumns.ts +493 -0
  175. package/src/lib/tableEnv.ts +34 -0
  176. package/src/lib/tablePrefs.ts +84 -0
  177. package/src/lib/tags.ts +121 -12
  178. package/src/lib/themes.ts +286 -18
  179. package/src/lib/tour.ts +152 -0
  180. package/src/lib/trashEmpty.ts +111 -0
  181. package/src/lib/trashTimeLeft.ts +23 -0
  182. package/src/lib/uiLocales.ts +460 -0
  183. package/src/lib/viewPrefs.ts +696 -660
  184. package/src/lib/viewPrefsHttp.ts +78 -0
  185. package/src/locales/en.ts +515 -117
  186. package/src/locales/resolve.ts +17 -8
  187. package/src/locales/tr.ts +536 -152
  188. package/src/modals/ConvertModal.vue +62 -11
  189. package/src/modals/DestinationPickerModal.vue +59 -16
  190. package/src/modals/Modal.vue +113 -23
  191. package/src/modals/NewDocumentModal.vue +16 -6
  192. package/src/modals/NewFolderModal.vue +8 -0
  193. package/src/modals/PermissionsModal.vue +272 -163
  194. package/src/modals/PreviewModal.vue +119 -20
  195. package/src/modals/RenameModal.vue +37 -1
  196. package/src/styles/base.css +2890 -260
  197. package/src/styles/sign-fonts.css +118 -0
  198. package/src/styles/variables.css +60 -0
  199. package/src/types/Connections.ts +17 -0
  200. package/src/types/ExplorerConfig.ts +117 -1
  201. package/src/types/FileNode.ts +50 -0
  202. package/src/types/Plugins.ts +494 -0
  203. package/src/types/Public.ts +212 -0
  204. package/src/viewers/ArchiveViewer.vue +93 -48
  205. package/src/viewers/CsvViewer.vue +99 -53
  206. package/src/viewers/DrawioViewer.vue +35 -9
  207. package/src/viewers/EpubViewer.vue +7 -8
  208. package/src/viewers/IpynbViewer.vue +3 -1
  209. package/src/viewers/MermaidViewer.vue +4 -5
  210. package/src/viewers/PdfViewer.vue +8 -17
  211. package/src/viewers/PsdViewer.vue +2 -4
  212. package/src/viewers/TiffViewer.vue +4 -5
  213. package/src/viewers/Viewer3D.vue +1 -1
  214. package/LICENSE +0 -21
  215. package/dist/ArchiveViewer-BUQWLhwX.js +0 -124
  216. package/dist/ArchiveViewer-BUQWLhwX.js.map +0 -1
  217. package/dist/CsvViewer-Bg2_aqzc.js +0 -141
  218. package/dist/CsvViewer-Bg2_aqzc.js.map +0 -1
  219. package/dist/DrawioViewer-CC6rJXeP.js.map +0 -1
  220. package/dist/EpubViewer-CAoTSXTO.js.map +0 -1
  221. package/dist/IpynbViewer-D0GwGpnE.js +0 -184
  222. package/dist/IpynbViewer-D0GwGpnE.js.map +0 -1
  223. package/dist/MermaidViewer-DGwAvRZg.js +0 -137
  224. package/dist/MermaidViewer-DGwAvRZg.js.map +0 -1
  225. package/dist/PsdViewer-DhGweA0m.js +0 -122
  226. package/dist/PsdViewer-DhGweA0m.js.map +0 -1
  227. package/dist/TiffViewer-D0AurvAh.js +0 -142
  228. package/dist/TiffViewer-D0AurvAh.js.map +0 -1
  229. package/dist/Viewer3D-CdZeK4j4.js.map +0 -1
  230. package/dist/index-BArI-B1M.js +0 -26911
  231. package/dist/index-BArI-B1M.js.map +0 -1
  232. package/dist/useViewerFetch-czqbd2Lj.js +0 -25
  233. package/dist/useViewerFetch-czqbd2Lj.js.map +0 -1
  234. package/src/composables/useScrolledX.ts +0 -21
package/src/locales/en.ts CHANGED
@@ -15,7 +15,7 @@ export const en: Record<string, string> = {
15
15
  'ctx.convert': 'Convert',
16
16
  'ctx.rename': 'Rename',
17
17
  'ctx.delete': 'Delete',
18
- 'ctx.delete_perm': 'Delete Permanently',
18
+ 'ctx.delete_perm': 'Delete permanently',
19
19
  'ctx.restore': 'Restore',
20
20
  'ctx.share': 'Share',
21
21
  'ctx.move': 'Move',
@@ -43,7 +43,7 @@ export const en: Record<string, string> = {
43
43
 
44
44
  'modal.delete.title': 'Delete?',
45
45
  'modal.delete.message': '{count} items will be moved to trash.',
46
- 'modal.delete.message_one': '1 item will be moved to trash.',
46
+ 'modal.delete.message_one': '{count} item will be moved to trash.',
47
47
  'modal.delete.confirm': 'Move to Trash',
48
48
  'modal.delete.cancel': 'Cancel',
49
49
 
@@ -56,7 +56,7 @@ export const en: Record<string, string> = {
56
56
  'modal.newfolder.create': 'Create',
57
57
  'modal.newfolder.cancel': 'Cancel',
58
58
 
59
- 'modal.share.title': 'Share Link',
59
+ 'modal.share.title': 'Share link',
60
60
  'modal.share.pin': 'PIN protected',
61
61
  'modal.share.expires': 'Expires at',
62
62
  'modal.share.max_downloads': 'Download limit',
@@ -121,8 +121,9 @@ export const en: Record<string, string> = {
121
121
  'viewer.open_in_new_tab': 'Open in new tab',
122
122
  'viewer.loading': 'Loading…',
123
123
  'viewer.failed_to_load': 'Failed to load file',
124
- 'viewer.peer_not_installed': 'Optional viewer library is not installed. Install the peer dependency or use the download button.',
125
- 'viewer.format_unsupported_3d': 'This 3D format is not supported in the browser preview (model-viewer only reads .glb / .gltf / .usdz). Please download and open with a local tool.',
124
+ /* ⚠ Said to a PERSON: it used to tell them to "install the peer dependency". */
125
+ 'viewer.peer_not_installed': 'This kind of file cannot be shown here. Download it to open it on your device.',
126
+ 'viewer.format_unsupported_3d': 'The preview shows 3D models in .glb, .gltf and .usdz only. Download the file to open it in a 3D program.',
126
127
  'viewer.zoom_in': 'Zoom in',
127
128
  'viewer.zoom_out': 'Zoom out',
128
129
  'viewer.fit_width': 'Fit width',
@@ -154,11 +155,20 @@ export const en: Record<string, string> = {
154
155
  'viewer.csv_filter': 'Filter rows…',
155
156
  'viewer.epub_prev': 'Previous',
156
157
  'viewer.epub_next': 'Next',
157
- 'viewer.drawio.disabled': 'diagrams.net embed is not configured for this filex instance — operator must wire FILEX_DRAWIO_URL to enable the editor.',
158
+ /* ⚠ The old sentence named an ENVIRONMENT VARIABLE to every reader
159
+ ("operator must wire FILEX_DRAWIO_URL"). A person who only wanted to look
160
+ at a diagram is told what they can do; `_admin` — someone who can fix it —
161
+ is told where. */
162
+ 'viewer.drawio.disabled': 'Diagrams cannot be opened here: this server has no draw.io. Download the file to open it elsewhere.',
163
+ 'viewer.drawio.disabled_admin': 'draw.io is not set up on this server. Set it up in the admin panel under External services.',
164
+ 'viewer.drawio.load_failed': 'This diagram could not be read. Download it to open it elsewhere.',
165
+ 'viewer.drawio.save_failed': 'The diagram could not be saved. Your changes are still in the editor — try again.',
158
166
  'viewer.archive.entries': '{n} files',
159
- 'viewer.archive.entries_one': '1 file',
167
+ 'viewer.archive.entries_one': '{n} file',
160
168
  'viewer.archive.empty': 'Archive is empty.',
161
169
  'viewer.archive.error': 'Could not read archive contents.',
170
+ 'viewer.archive.unsupported': 'This kind of archive cannot be listed here. Download it to see what is inside.',
171
+ 'viewer.archive.too_large': 'This archive is too large to list here. Download it to see what is inside.',
162
172
  'viewer.name': 'Name',
163
173
  'viewer.size': 'Size',
164
174
 
@@ -166,8 +176,8 @@ export const en: Record<string, string> = {
166
176
  'col.sort': 'Sort',
167
177
  'group.today': 'Today',
168
178
  'group.yesterday': 'Yesterday',
169
- 'group.this_week': 'This Week',
170
- 'group.this_month': 'This Month',
179
+ 'group.this_week': 'This week',
180
+ 'group.this_month': 'This month',
171
181
  'group.no_date': 'No date',
172
182
  'toolbar.density.compact': 'Compact view',
173
183
  'toolbar.density.comfortable': 'Comfortable view',
@@ -181,7 +191,7 @@ export const en: Record<string, string> = {
181
191
  'palette.hint': '↑↓ navigate · Enter select · Esc close',
182
192
  'cmd.view_toggle': 'Toggle view (list ⇄ grid)',
183
193
  'cmd.trash': 'Open trash',
184
- 'shortcuts.title': 'Keyboard Shortcuts',
194
+ 'shortcuts.title': 'Keyboard shortcuts',
185
195
  'shortcuts.group.nav': 'Navigation',
186
196
  'shortcuts.group.selection': 'Selection',
187
197
  'shortcuts.group.file': 'File operations',
@@ -199,7 +209,7 @@ export const en: Record<string, string> = {
199
209
  'shortcuts.cut': 'Cut',
200
210
  'shortcuts.copy': 'Copy',
201
211
  'shortcuts.paste': 'Paste',
202
- 'breadcrumb.more': 'Hidden folders',
212
+ 'breadcrumb.more': 'Folders in between',
203
213
 
204
214
  // ——— State screens + undo snackbar + connection badge (cila:b) ———
205
215
  'empty.hint': 'Drag and drop files here',
@@ -218,6 +228,40 @@ export const en: Record<string, string> = {
218
228
  /* i18n:hardcoded — toast copy that used to be Turkish string literals in
219
229
  FileExplorer.vue, unreachable by any locale. */
220
230
  'toast.failed': 'Operation failed',
231
+ /* ── errorWords (lib/errorWords): how a failure is SAID. ⚠ No status code,
232
+ no JSON, no environment variable ever reaches these; an administrator
233
+ sees the raw words on a second line, nobody else does. ── */
234
+ 'err.status.400': 'Bad request',
235
+ 'err.status.401': 'Sign-in required',
236
+ 'err.status.403': 'You are not allowed to do this',
237
+ 'err.status.404': 'Not found',
238
+ 'err.status.409': 'Already exists / conflict',
239
+ 'err.status.413': 'File too large',
240
+ 'err.status.415': 'Unsupported file type',
241
+ 'err.status.422': 'Invalid data',
242
+ 'err.status.429': 'Too many requests',
243
+ 'err.status.500': 'Server error',
244
+ 'err.status.501': 'Not supported',
245
+ 'err.status.502': 'A connected service reported an error',
246
+ 'err.status.503': 'Service unavailable',
247
+ 'err.status.504': 'A connected service took too long to answer',
248
+ 'err.status.other': 'The server refused this request.',
249
+ 'err.upload_stalled': 'The upload stopped making progress. Try again — it continues where it stopped.',
250
+ 'err.network': 'The server could not be reached. Check your connection and try again.',
251
+ 'err.read_only': 'This storage is read-only',
252
+ 'err.no_secret_key': 'This server has no encryption key set, so it cannot issue an access key. Ask an administrator to configure one.',
253
+ 'err.quota': 'Your storage quota is full.',
254
+ 'err.save_failed': 'Your changes could not be saved.',
255
+ 'err.load_failed': 'This file could not be loaded.',
256
+ 'err.viewer_failed': 'The viewer for this file could not be started. Download the file to open it.',
257
+ 'err.detail': 'Details',
258
+ 'opc.err.timeout': 'The app took too long and was stopped.',
259
+ 'opc.err.out_of_memory': 'The app ran out of memory on this file.',
260
+ 'opc.err.crashed': 'The app stopped unexpectedly.',
261
+ 'opc.err.app_removed': 'The app was removed before this job could run.',
262
+ 'opc.err.action_removed': 'This action no longer exists — the app was updated.',
263
+ 'opc.err.engine_missing': 'This needs a program the server does not have ({engine}). Ask your administrator.',
264
+ 'opc.err.engine_missing_admin': 'This needs {engine}, which is not installed on the server that runs filex. Install it there and try again.',
221
265
  'toast.copied': 'Copied',
222
266
  'toast.deleted': 'Deleted',
223
267
  'toast.cut': 'Cut',
@@ -225,20 +269,39 @@ export const en: Record<string, string> = {
225
269
  'toast.copy_ready': 'Copied — ready to paste',
226
270
  'toast.same_folder_cut': 'Cannot cut into the same folder',
227
271
  'toast.restored': '{n} items restored',
228
- 'toast.restored_one': '1 item restored',
272
+ 'toast.restored_one': '{n} item restored',
229
273
  'toast.restore_taken': '{n} items were not restored: something already has their names. Rename what is there, then restore again.',
230
274
  'toast.restore_taken_one': '“{name}” was not restored: something already has that name. Rename what is there, then restore again.',
231
275
  'toast.delete_queued': 'Delete queued',
232
276
  'toast.trash_retention': 'Items in the trash are removed automatically once the retention window ends. Permanent deletion is done from the admin panel.',
233
277
  'dropzone.hint': 'Drop files here',
234
278
  'modal.newfolder.invalid': 'Invalid character',
235
- 'viewer.office_unconfigured': 'OnlyOffice is not configured',
279
+ 'names.reserved': '“{name}” is reserved for filex’s own use. Choose another name.',
280
+ /* ⚠ A missing or broken document server is said in words, never as the
281
+ HTTP layer (it used to read `Config fetch 503: {"error":"onlyoffice not
282
+ configured"}`). `_admin` is what someone who can fix it reads — where to
283
+ fix it; everybody else reads how to get the file anyway. */
284
+ 'viewer.office_unconfigured':
285
+ 'Office documents cannot be opened here: this server has no ONLYOFFICE document server. Download the file to open it on your computer.',
286
+ 'viewer.office_unconfigured_admin':
287
+ 'ONLYOFFICE is not set up on this server, so office documents cannot be opened here. Set it up in the admin panel under External services.',
288
+ 'viewer.office_unreachable': 'The document server is not answering right now. Try again later, or download the file.',
289
+ 'viewer.office_unreachable_admin':
290
+ 'The ONLYOFFICE document server is not answering. Check it in the admin panel under External services.',
291
+ 'viewer.office_failed': 'This document could not be opened in ONLYOFFICE. Download it to open it on your computer.',
292
+ 'viewer.office_forbidden': 'You do not have permission to open this document in ONLYOFFICE.',
293
+ /* The explorer's Open on a document the document server would open, when
294
+ there is none — shown to an administrator only (everyone else is not
295
+ offered the entry at all; lib/serviceGate). */
296
+ 'ctx.needs_onlyoffice': 'ONLYOFFICE is not set up — set it up in the admin panel under External services.',
297
+ 'ctx.needs_drawio': 'draw.io is not set up — set it up in the admin panel under External services.',
298
+ 'ctx.needs_convert': 'The converter is not set up or not answering — check it in the admin panel under External services.',
236
299
  'viewer.pdf_inline_failed': 'Your browser could not display this PDF inline.',
237
300
  'viewer.too_large': 'File is too large (>1 MB).',
238
301
  'viewer.csv_rows': '{n} rows',
239
- 'viewer.csv_rows_one': '1 row',
302
+ 'viewer.csv_rows_one': '{n} row',
240
303
  'presence.others': '{n} people',
241
- 'presence.others_one': '1 person',
304
+ 'presence.others_one': '{n} person',
242
305
  'conn.offline': 'No live connection — changes may be delayed',
243
306
  'conn.tooltip': 'Live connection to the server is unavailable; the list refreshes periodically. This notice disappears once the connection is back.',
244
307
 
@@ -249,6 +312,7 @@ export const en: Record<string, string> = {
249
312
  'palette.save': 'Save search',
250
313
  'palette.saved.delete': 'Delete saved search',
251
314
  'search.in_content': 'In content',
315
+ 'search.truncated': 'More results than shown — narrow your search',
252
316
 
253
317
  /* === bag:b4 === */
254
318
  'toolbar.more': 'More actions',
@@ -267,7 +331,7 @@ export const en: Record<string, string> = {
267
331
  'inspector.title': 'Details',
268
332
  'inspector.copy_name': 'Copy name',
269
333
  'inspector.items': '{n} items',
270
- 'inspector.items_one': '1 item',
334
+ 'inspector.items_one': '{n} item',
271
335
  'inspector.type': 'Type',
272
336
  'inspector.select_hint': 'Select an item to see its details.',
273
337
  /* pane:p1 — the panel HOLDS the last selected thing (owner's ruling,
@@ -284,6 +348,19 @@ export const en: Record<string, string> = {
284
348
  'tags.add': '+ Add tag',
285
349
  'tags.name': 'Tag name',
286
350
  'tags.remove': 'Remove tag',
351
+
352
+ /* etiket:k2 (v0.43.0) — the two KINDS of tag. Every place a tag is drawn
353
+ says which kind it is, in words as well as the icon, because until this
354
+ release a tag was silently shared with every account on the server. */
355
+ 'tags.kind.label': 'Who sees this tag',
356
+ 'tags.kind.personal': 'Personal',
357
+ 'tags.kind.team': 'Team',
358
+ 'tags.kind.personal_help': 'Only you see it',
359
+ 'tags.kind.team_help': 'Everyone on your team who can see this file',
360
+ 'tags.kind.team_locked': 'Adding or removing a team tag needs edit permission on this file.',
361
+ 'tags.chip.personal': 'Personal tag “{tag}” — only you see it',
362
+ 'tags.chip.team': 'Team tag “{tag}” — everyone on your team who can see this file sees it',
363
+ 'tags.add_submit': 'Add',
287
364
  'inspector.section.versions': 'Versions',
288
365
  'inspector.section.permissions': 'Permissions',
289
366
  'inspector.section.shares': 'Shares',
@@ -297,9 +374,9 @@ export const en: Record<string, string> = {
297
374
  'inspector.copied': 'Copied',
298
375
  'inspector.error': 'Could not load',
299
376
  'inspector.items_summary': '{n} items, {size} total',
300
- 'inspector.items_summary_one': '1 item, {size} total',
377
+ 'inspector.items_summary_one': '{n} item, {size} total',
301
378
  'inspector.folder_items': '{n} items',
302
- 'inspector.folder_items_one': '1 item',
379
+ 'inspector.folder_items_one': '{n} item',
303
380
  'inspector.versions.empty': 'No version history',
304
381
  'inspector.versions.v': 'Version {n}',
305
382
  'inspector.versions.restore': 'Restore',
@@ -321,8 +398,8 @@ export const en: Record<string, string> = {
321
398
 
322
399
  /* wiring:c1 — theme gallery */
323
400
  'theme.menu': 'Theme',
324
- 'theme.title': 'Theme Gallery',
325
- 'theme.hint': 'A theme only changes the colour palette — day or night is the choice above.',
401
+ 'theme.title': 'Theme gallery',
402
+ 'theme.hint': 'A theme only changes the color palette — day or night is the choice above.',
326
403
  'theme.mode.label': 'Appearance',
327
404
  'theme.mode.light': 'Day',
328
405
  'theme.mode.dark': 'Night',
@@ -331,13 +408,13 @@ export const en: Record<string, string> = {
331
408
  'theme.close': 'Close',
332
409
  'theme.selected': 'Selected',
333
410
  'theme.name.default': 'Default',
334
- 'theme.name.night': 'Night Blue',
411
+ 'theme.name.night': 'Night blue',
335
412
  'theme.name.forest': 'Forest',
336
413
  'theme.name.amber': 'Amber',
337
414
  'theme.name.lilac': 'Lilac',
338
- 'theme.name.contrast': 'High Contrast',
339
- 'theme.name.gray': 'Soft Gray',
340
- 'theme.name.terminal': 'Terminal Green',
415
+ 'theme.name.contrast': 'High contrast',
416
+ 'theme.name.gray': 'Soft gray',
417
+ 'theme.name.terminal': 'Terminal green',
341
418
  /* === zaman:z3 — the time-zone picker (embed dialog + web settings) === */
342
419
  'tz.menu': 'Time zone',
343
420
  'tz.title': 'Time zone',
@@ -354,7 +431,7 @@ export const en: Record<string, string> = {
354
431
  'shortcuts.quicklook': 'Quick look',
355
432
  'shortcuts.customize': 'Customize',
356
433
  'shortcuts.settings.menu': 'Shortcuts',
357
- 'shortcuts.settings.title': 'Shortcut Settings',
434
+ 'shortcuts.settings.title': 'Shortcut settings',
358
435
  'shortcuts.settings.hint': 'To remap a shortcut, press "Change" and then the new key combination. Changes are stored in this browser only.',
359
436
  'shortcuts.settings.change': 'Change',
360
437
  'shortcuts.settings.capture': 'Press keys… (Esc cancels)',
@@ -374,7 +451,7 @@ export const en: Record<string, string> = {
374
451
  /* === wiring:c3 — operations center === */
375
452
  'opc.title': 'Operations',
376
453
  'opc.aria_badge': '{n} operations — toggle operations center',
377
- 'opc.aria_badge_one': '1 operation — toggle operations center',
454
+ 'opc.aria_badge_one': '{n} operation — toggle operations center',
378
455
  'opc.close': 'Close',
379
456
  'opc.active': 'In progress',
380
457
  'opc.history': 'History',
@@ -395,6 +472,211 @@ export const en: Record<string, string> = {
395
472
  'opc.kind.delete': 'Delete',
396
473
  'opc.kind.convert': 'Convert',
397
474
  'opc.kind.archive': 'Archive',
475
+ 'opc.kind.plugin': 'App',
476
+ 'opc.kind.trash': 'Empty trash',
477
+ 'opc.open': 'Open',
478
+
479
+ /* App plugins — rows a WebAssembly plugin adds to the file menu, and the
480
+ jobs they queue (docs/APP-PLUGINS-API.md). "App" is what the feature is
481
+ called on every surface; "plugin" is the wire word. */
482
+ 'plugin.queued': '{label} queued',
483
+ 'plugin.done': '{label} finished',
484
+ 'plugin.failed': '{label} failed',
485
+ 'plugin.cancel_failed': 'Could not cancel the job',
486
+ 'plugin.not_applicable': 'This action does not apply to the selection',
487
+ 'plugin.read_only': 'This storage is read-only',
488
+ 'plugin.needs_engine': '{name} is not installed on this server — install it and restart filex.',
489
+ 'plugin.needs_other': '{name} is not available on this server.',
490
+ 'plugin.confirm.title': 'Confirm',
491
+ 'plugin.confirm.cancel': 'Cancel',
492
+ 'plugin.confirm.run': 'Run',
493
+ 'plugin.view.close': 'Close',
494
+ 'plugin.view.busy': 'Working…',
495
+ 'plugin.view.unsupported': 'This screen uses a component this version cannot draw ({type})',
496
+ 'plugin.view.error': 'The app answered with an error',
497
+ 'plugin.output_missing': 'The output is no longer where it was',
498
+ /* M2 — surface components (components/plugin/nodes/*) */
499
+ 'plugin.view.loading': 'Loading…',
500
+ 'plugin.view.preview_failed': 'The preview could not be loaded',
501
+ 'plugin.steps.label': 'Steps',
502
+ 'plugin.list.empty': 'Nothing here yet',
503
+ 'plugin.list.actions': 'Actions',
504
+ /* The label on a table row's ONE action control (core RowActions.vue).
505
+ The explorer's handle is the ⋮ glyph; a panel row's says what it is.
506
+ Owner, 2026-09-20: "adminde 'Aksiyon' diye yazmalı". */
507
+ 'row.actions': 'Actions',
508
+ 'plugin.people.placeholder': 'Name or email',
509
+ 'plugin.people.placeholder_email': 'Email address',
510
+ 'plugin.people.add': 'Add',
511
+ 'plugin.people.remove': 'Remove {name}',
512
+ 'plugin.people.invalid_email': 'Enter a valid email address',
513
+ 'plugin.people.external_off': 'Only people with an account can be added',
514
+ 'plugin.pin.label': 'PIN',
515
+ 'plugin.file.choose': 'Choose…',
516
+ 'plugin.file.clear': 'Clear',
517
+ 'plugin.file.none': 'Nothing chosen',
518
+ /* M3 — the sign track: signature-pad + pdf-fields nodes, and the outside
519
+ participant's public page (views/PublicSurface.vue via usePublicPage). */
520
+ 'plugin.sig.mode_draw': 'Draw',
521
+ 'plugin.sig.mode_type': 'Type',
522
+ 'plugin.sig.mode_upload': 'Upload',
523
+ 'plugin.sig.draw_hint': 'Sign here with your finger or mouse',
524
+ 'plugin.sig.type_placeholder': 'Type your name',
525
+ 'plugin.sig.upload_pick': 'Choose a picture…',
526
+ 'plugin.sig.upload_hint': 'PNG or JPEG, at most {kb} KB',
527
+ 'plugin.sig.upload_too_big': 'The picture is larger than {kb} KB',
528
+ 'plugin.sig.upload_bad_type': 'Only PNG or JPEG pictures can be used',
529
+ 'plugin.sig.upload_unreadable': 'The picture could not be read',
530
+ 'plugin.sig.clear': 'Clear signature',
531
+ 'plugin.sig.signed': 'Signature captured',
532
+ 'plugin.pdf.load_failed': 'The document could not be loaded',
533
+ 'plugin.pdf.palette': 'Field types',
534
+ 'plugin.pdf.pending': 'Boxes waiting for a place',
535
+ 'plugin.pdf.place_box_hint': 'Tap the page where “{name}” goes, or drag to size it',
536
+ 'plugin.pdf.all_placed': 'Every box has a place',
537
+ 'plugin.pdf.place_idle': 'Choose one of the boxes above, then tap the page where it goes.',
538
+ 'plugin.pdf.place_idle_done': 'Tap a box on the page to take it off or copy it; drag it to move it.',
539
+ 'plugin.pdf.no_boxes': 'No boxes yet. Add one below: give it a name, say whose it is, and place it on the document in the next step.',
540
+ 'plugin.pdf.add_box': 'Add a box',
541
+ 'plugin.pdf.unplace': 'Take off the page',
542
+ 'plugin.pdf.place_hint': 'Tap the page where the {type} field goes, or drag to size it',
543
+ 'plugin.pdf.type_signature': 'Signature',
544
+ 'plugin.pdf.type_initials': 'Initials',
545
+ 'plugin.pdf.type_date': 'Date',
546
+ 'plugin.pdf.type_text': 'Text',
547
+ 'plugin.pdf.type_checkbox': 'Checkbox',
548
+ 'plugin.pdf.tap_signature': 'Tap to sign',
549
+ 'plugin.pdf.tap_initials': 'Tap to initial',
550
+ 'plugin.pdf.tap_date': 'Tap for today',
551
+ 'plugin.pdf.tap_text': 'Text',
552
+ 'plugin.pdf.tap_checkbox': 'Tap to tick',
553
+ 'plugin.pdf.label': 'Name',
554
+ 'plugin.pdf.assignee': 'Signer',
555
+ 'plugin.pdf.unassigned': 'Anyone',
556
+ 'plugin.pdf.filled_by': 'Filled by',
557
+ 'plugin.pdf.style': 'How it is signed',
558
+ 'plugin.pdf.style_drawn': 'Drawn',
559
+ 'plugin.pdf.style_typed': 'Typed',
560
+ 'plugin.pdf.lines': 'Printed under it',
561
+ 'plugin.pdf.lines_preview': 'Under the signature:',
562
+ 'plugin.pdf.lines_none': 'Nothing is printed under this signature.',
563
+ 'plugin.pdf.required': 'Required',
564
+ 'plugin.pdf.copy_to_page': 'Copy to page',
565
+ 'plugin.pdf.copy': 'Copy',
566
+ 'plugin.pdf.delete': 'Delete field',
567
+ 'plugin.pdf.zoom_in': 'Zoom in',
568
+ 'plugin.pdf.zoom_out': 'Zoom out',
569
+ 'plugin.pdf.zoom_fit': 'Fit to width',
570
+ 'plugin.pdf.zoom_fit_page': 'Fit the page to the screen',
571
+ 'plugin.pdf.pad_for': 'Your {type}',
572
+ 'plugin.pdf.pad_done': 'Done',
573
+ 'plugin.page.loading': 'Loading…',
574
+ 'plugin.page.pin_title': 'This page is protected',
575
+ 'plugin.page.pin_hint': 'Enter the PIN you were given to continue',
576
+ 'plugin.page.pin_submit': 'Continue',
577
+ 'plugin.page.pin_wrong': 'That PIN is not right',
578
+ 'plugin.page.pin_locked': 'Too many wrong PINs — wait a few minutes and try again',
579
+ 'plugin.page.unavailable_title': 'This link is not available',
580
+ 'plugin.page.unavailable_text': 'It may have expired, been used up or been withdrawn. Ask the person who sent it for a new one.',
581
+ 'plugin.page.accepted_title': 'Received, thank you',
582
+ 'plugin.page.accepted_text': 'Your answer has been recorded. You can close this page.',
583
+ 'plugin.page.done_title': 'All done',
584
+ 'plugin.page.done_text': 'There is nothing more to do here. You can close this page.',
585
+ 'plugin.page.error_title': 'Something went wrong',
586
+ 'plugin.page.retry': 'Try again',
587
+ 'plugin.page.files': 'Documents',
588
+ 'plugin.page.open_file': 'Open {name} in a new tab',
589
+ 'plugin.page.footer': 'Served by filex',
590
+ /* === v3 — the ONE public shell: a share, a file request and an app's page
591
+ are one page with three bodies. These are the words the shell itself
592
+ says, so they are the same words whichever link was followed. === */
593
+ 'public.served_by': 'Served by {name}',
594
+ 'public.expires': 'The link stops working on {when}',
595
+ 'public.visits_left': '{n} visits left',
596
+ 'public.visits_left_one': '{n} visit left',
597
+ 'public.language_from': 'Added by the {app} app',
598
+ 'public.up': 'Up one level',
599
+ 'public.download_all': 'Download everything',
600
+ 'public.browse': 'Look inside',
601
+ 'public.folder_empty': 'There is nothing in this folder',
602
+ 'public.drop_sub': 'Drop your files below, or pick them. You can only add to this folder — what is already in it stays hidden from you.',
603
+ 'public.drop_here': 'Drop the files here',
604
+ 'public.or_choose': 'or click to choose them',
605
+ 'public.max_size': 'At most {size} per file',
606
+ 'public.accepts': 'Accepted: {list}',
607
+ 'public.files_left': '{n} more files can be sent',
608
+ 'public.files_left_one': '{n} more file can be sent',
609
+ 'public.request_full': 'Everything that was asked for has arrived',
610
+ 'public.upload_done': 'Sent',
611
+ 'public.upload_failed': 'Could not be sent',
612
+ 'public.your_name': 'Your name (optional)',
613
+ 'public.your_name_ph': 'e.g. Alex Smith',
614
+ 'public.refused_ext': 'Not sent — this link does not accept this type of file.',
615
+ 'public.refused_too_large': 'Not sent — larger than {size}.',
616
+ 'public.refused_too_many': 'Not sent — at most {count} files can be sent now.',
617
+ 'public.refused_too_many_one': 'Not sent — only {count} file can be sent now.',
618
+ /* === App plugins v2 — a `page` view, an app's lock on a file, the output
619
+ mode a job wrote in, and the faces + rules a signature field carries. === */
620
+ 'plugin.page_view.back': 'Back to the files',
621
+ 'plugin.page_view.queued_title': 'The job is queued',
622
+ 'plugin.page_view.queued_text': 'filex is working on it. Its progress is in the operations tray, and you will be told when it lands.',
623
+ 'plugin.page_view.open_ops': 'Open the operations tray',
624
+ 'plugin.page_view.done_title': 'All done',
625
+ 'plugin.page_view.done_text': 'There is nothing more to do here. You can close this tab.',
626
+ 'plugin.page_view.error_title': 'This screen could not be opened',
627
+ 'plugin.page_view.retry': 'Try again',
628
+ 'plugin.page_view.blocked': 'The new tab was blocked — allow pop-ups for this site, or the screen will open in a dialog',
629
+ 'plugin.output.sibling': 'new file',
630
+ 'plugin.output.version': 'new version',
631
+ 'plugin.output.sibling_title': 'The result lands as a new file beside the original',
632
+ 'plugin.output.version_title': 'The result replaces the file as a new version of it',
633
+ 'plugin.sign.font_label': 'Handwriting',
634
+ 'plugin.pdf.font': 'Font',
635
+ 'plugin.pdf.rule_number': 'Numbers only',
636
+ 'plugin.pdf.rule_email': 'Email address',
637
+ 'plugin.pdf.rule_length': 'Between {min} and {max} characters',
638
+ 'plugin.pdf.rule_min': 'At least {min} characters',
639
+ 'plugin.pdf.rule_max': 'At most {max} characters',
640
+ 'plugin.pdf.err_number': 'Enter a number',
641
+ 'plugin.pdf.err_email': 'Enter a valid email address',
642
+ 'plugin.pdf.err_min': 'Too short — at least {min} characters',
643
+ 'plugin.pdf.err_max': 'Too long — at most {max} characters',
644
+ 'applock.badge': 'Locked',
645
+ 'applock.some_app': 'an app',
646
+ 'applock.held': '{app} locked this file',
647
+ 'applock.held_reason': '{app} locked this file: {reason}',
648
+ 'applock.until': '— until {date}',
649
+ 'applock.inspector': 'Held by an app',
650
+ /* issue #34 — a symlink the server will NOT follow (lib/symlink). The badge
651
+ is the short word on the row; `why` is the sentence behind it, shown on
652
+ hover, in the details panel and in the toast when somebody tries to open
653
+ one. ⚠ Broken and outside-of-root are DIFFERENT states and must read
654
+ differently: only one has an administrator remedy, and offering that
655
+ remedy for a target that has been deleted sends the person to a settings
656
+ screen that cannot help them. */
657
+ 'symlink.badge.outside_root': 'Outside storage',
658
+ 'symlink.badge.broken': 'Broken link',
659
+ 'symlink.badge.unresolved': 'Remote link',
660
+ 'symlink.badge.unknown': 'Link',
661
+ 'symlink.why.outside_root':
662
+ 'A link whose target lies outside this storage, which is why it cannot be opened. An administrator can allow it with "Follow symlinks that leave this folder" in the storage\'s settings.',
663
+ 'symlink.why.broken':
664
+ 'A link whose target no longer exists, so there is nothing to open. It has to be repaired or removed on the server — no setting can bring the target back.',
665
+ 'symlink.why.unresolved':
666
+ 'A link on a remote server. filex cannot tell whether its target is inside this storage, so it does not follow it and cannot open it.',
667
+ 'symlink.why.unknown':
668
+ 'A link filex will not follow, so it cannot be opened. Its target is either outside this storage, missing, or on a remote server filex cannot resolve.',
669
+ 'symlink.inspector': 'A link that cannot be opened',
670
+ 'plugin.pdf.rule': 'Accepts',
671
+ 'plugin.pdf.rule_kind_any': 'Any text',
672
+ 'plugin.pdf.rule_kind_number': 'Numbers only',
673
+ 'plugin.pdf.rule_kind_email': 'Email address',
674
+ 'plugin.pdf.rule_min_label': 'Min length',
675
+ 'plugin.pdf.rule_max_label': 'Max length',
676
+ 'plugin.pdf.format': 'Date layout',
677
+ 'plugin.pdf.date_order': 'Date order',
678
+ 'plugin.pdf.date_separator': 'Separator',
679
+ 'plugin.pdf.date_example': 'Example',
398
680
  /* === wiring:c4 — onboarding tour + accessibility + error card === */
399
681
  'tour.aria': 'Onboarding tour',
400
682
  'tour.restart': 'Restart the tour',
@@ -421,13 +703,13 @@ export const en: Record<string, string> = {
421
703
  'This trail is the address of the folder on screen. Click any step to go back up, and the chevron beside the last one lists the folders inside it.',
422
704
  'tour.step.filters.title': 'Narrow the list',
423
705
  'tour.step.filters.desc':
424
- 'Type, People, Modified and Size filter the folder you are in, the box beside them matches names as you type, and the control at the end of the row sorts.',
706
+ 'Type, Owner, Modified and Size filter the folder you are in, the box beside them matches names as you type, and the control at the end of the row sorts.',
425
707
  'tour.step.view.title': 'Switch views',
426
708
  'tour.step.view.desc':
427
709
  'Change how this folder is drawn; your choice is remembered.',
428
710
  'tour.step.details.title': 'Details & sharing',
429
711
  'tour.step.details.desc':
430
- 'Select a file and open this panel for its details, its activity and its share links. For the full menu, right-click the file or use the button at the end of its row — "Share / Permissions" there creates a link with an optional PIN and expiry.',
712
+ 'Select a file and open this panel for its details, its activity and its share links. For the full menu, right-click the file or use the button at the end of its row — "Share" there creates a link with an optional PIN and expiry.',
431
713
  'tour.step.help.title': 'Shortcuts',
432
714
  'tour.step.help.desc':
433
715
  'Press {help} for the shortcut cheat-sheet and {palette} for the command palette. Theme and "Restart the tour" are in the "More actions" menu at the top right; your account and sign-out are behind the avatar beside it.',
@@ -456,7 +738,7 @@ export const en: Record<string, string> = {
456
738
  'shortcuts.refresh': 'Refresh the listing',
457
739
  'shortcuts.download': 'Download',
458
740
  'shortcuts.preview': 'Preview',
459
- 'shortcuts.share': 'Share / permissions',
741
+ 'shortcuts.share': 'Share',
460
742
  'shortcuts.tags': 'Tags',
461
743
  'shortcuts.convert': 'Convert',
462
744
  'shortcuts.open_tab': 'Open in a new tab',
@@ -576,7 +858,7 @@ export const en: Record<string, string> = {
576
858
  'e2e.recover.escrow_required': 'Paste the escrow private key.',
577
859
  'e2e.recover.wrong_recovery': 'That recovery key does not open this folder.',
578
860
  'e2e.recover.wrong_escrow':
579
- 'That escrow key does not open this folder. Folders created before escrow was enabled have no escrow key, and cannot be given one.',
861
+ 'That escrow key does not open this folder. A folder created before escrow was turned on has no escrow slot until its owner grants one — until then, use the password or the recovery key.',
580
862
  'e2e.recover.bad_escrow_key': 'That is not a readable private key (expected PKCS#8, base64 or PEM).',
581
863
  'e2e.recover.notify_failed':
582
864
  'Could not notify the folder owner, so the folder was not unlocked. Escrow use is always announced.',
@@ -623,48 +905,14 @@ export const en: Record<string, string> = {
623
905
  "Base path" in the admin panel and something else in the desktop
624
906
  app would be the same drift this whole mechanism exists to stop. */
625
907
  'conn.title': 'Storage connections',
626
- 'conn.subtitle': 'Connect {host} to a backend, or connect your computer to {host}.',
908
+ 'conn.subtitle': 'How to reach {host} from your own computer: mount it, or point a client at it.',
627
909
  'conn.close': 'Close',
628
- 'conn.tab.storages': 'Storages',
629
- 'conn.tab.connect': 'How to connect',
630
- 'conn.loading': 'Loading…',
631
- 'conn.visibleOnly': 'you can browse this one',
632
- 'conn.denied.title': 'Only an administrator can add storages here',
633
- 'conn.denied.none': 'Your account may browse files but not change what this server is connected to. Ask an administrator of this server to add or edit a storage.',
634
- 'conn.denied.anonymous': 'You are not signed in on this server.',
635
- 'conn.denied.unreachable': 'The server did not answer: {error}',
636
- 'conn.denied.guideHint': 'You do not need administrator rights to connect your own computer to the storages you can already see.',
637
- 'conn.denied.guideCta': 'Show me how to connect',
638
- 'conn.list.count': '{n} storages',
639
- 'conn.list.count_one': '1 storage',
640
- 'conn.list.add': 'Add a storage',
641
- 'conn.list.empty': 'No storage is configured yet. Add one and the files show up in the explorer.',
642
- 'conn.list.edit': 'Edit',
643
- 'conn.list.remove': 'Remove',
644
- 'conn.list.confirm': 'Really remove?',
645
- 'conn.list.readOnly': 'read-only',
646
- 'conn.list.disabled': 'disabled',
647
- 'conn.form.newTitle': 'New storage',
648
- 'conn.form.editTitle': 'Edit “{name}”',
649
- 'conn.form.name': 'Display name',
650
- 'conn.form.namePlaceholder': 'photos',
651
- 'conn.form.nameHelp': 'What this storage is called everywhere: in the explorer, in paths (name://folder) and in WebDAV URLs.',
652
- 'conn.form.driver': 'Type',
653
- 'conn.form.driverLocked': 'A storage\'s type cannot change after it is created — the paths already point at it.',
654
- 'conn.form.readOnly': 'Read-only (nothing may be written to this storage)',
655
- 'conn.form.enabled': 'Enabled',
656
- 'conn.form.test': 'Test connection',
657
- 'conn.form.testing': 'Testing…',
658
- 'conn.form.testOk': 'Connected. {count} items at the root.',
659
- 'conn.form.testOk_one': 'Connected. 1 item at the root.',
660
- 'conn.form.testFail': 'Could not connect: {error}',
661
- 'conn.form.save': 'Save',
662
- 'conn.form.saving': 'Saving…',
663
- 'conn.form.cancel': 'Cancel',
664
- 'conn.form.nameRequired': 'Give the storage a name.',
665
- 'conn.form.fillRequired': 'Fill in the fields marked with *.',
910
+ 'conn.list.disabled': 'Disabled',
666
911
  'conn.form.required': 'Required',
667
912
  'conn.form.advanced': 'Advanced settings',
913
+ /* A two-button decision's own words, when the declaration did not name them. */
914
+ 'conn.form.yes': 'Yes',
915
+ 'conn.form.no': 'No',
668
916
  'conn.form.noFields': 'This server did not describe any settings for this driver.',
669
917
  'conn.form.reveal': 'Show',
670
918
  'conn.form.hide': 'Hide',
@@ -675,15 +923,15 @@ export const en: Record<string, string> = {
675
923
  'conn.guide.copied': 'Copied',
676
924
  'conn.guide.goodToKnow': 'Good to know',
677
925
  'conn.guide.userPlaceholder': 'your-email@example.com',
678
- 'conn.guide.secretPlaceholder': 'your-password-or-token',
926
+ 'conn.guide.secretPlaceholder': 'your-password-or-api-key',
679
927
  'conn.guide.fact.url': 'Address',
680
928
  'conn.guide.fact.urlRootHint': 'Every storage you can see appears as a folder under this address.',
681
929
  'conn.guide.fact.urlStorageHint': 'Mounts “{storage}” directly. Drop the last segment to see every storage.',
682
930
  'conn.guide.fact.user': 'Username',
683
931
  'conn.guide.fact.password': 'Password',
684
932
  'conn.guide.webdav.summary': 'WebDAV mounts filex as a drive: Windows Explorer, macOS Finder, Linux, rclone and Cyberduck all speak it, and every change is mirrored back into filex.',
685
- 'conn.guide.webdav.userHint': 'Your filex account e-mail — not a display name.',
686
- 'conn.guide.webdav.passwordHint': 'Your account password, or an API token (Settings → API tokens). An account with two-factor authentication MUST use a token: Basic auth has no second-factor slot.',
933
+ 'conn.guide.webdav.userHint': 'Your username or your email — not your display name.',
934
+ 'conn.guide.webdav.passwordHint': 'Your account password, or an API key (API keys, in the navigation panel). An account with two-factor authentication MUST use an API key: Basic auth has no second-factor slot.',
687
935
  'conn.guide.webdav.win.s1': 'Open File Explorer, right-click This PC and choose “Map network drive…”.',
688
936
  'conn.guide.webdav.win.s2': 'Pick a drive letter and enter {url} as the folder.',
689
937
  'conn.guide.webdav.win.s3': 'Tick “Connect using different credentials” and sign in with the username and password above.',
@@ -806,11 +1054,11 @@ export const en: Record<string, string> = {
806
1054
  'conn.guide.sftp.fact.hostHint': 'The same machine as the web app, on a port of its own.',
807
1055
  'conn.guide.sftp.fact.port': 'Port',
808
1056
  'conn.guide.sftp.fact.portHint': 'Not 22 and not 443: SFTP is raw TCP and cannot go through the web proxy.',
809
- 'conn.guide.sftp.fact.userHint': 'Your short login name. An @ in a login has to be quoted in most client config files, which is what this avoids.',
1057
+ 'conn.guide.sftp.fact.userHint': 'Your username. An @ in it would have to be quoted in most client config files, which is what the username avoids.',
810
1058
  'conn.guide.sftp.fact.auth': 'Sign in with',
811
1059
  'conn.guide.sftp.fact.authKey': 'your registered SSH key (or your password)',
812
1060
  'conn.guide.sftp.fact.authPassword': 'your account password — or register a key above',
813
- 'conn.guide.sftp.fact.authHint': 'An API token works as a password too, and is revocable on its own. An account with two-factor sign-in must use a key or a token.',
1061
+ 'conn.guide.sftp.fact.authHint': 'An API key works as a password too, and is revocable on its own. An account with two-factor sign-in must use an SSH key or an API key.',
814
1062
  'conn.guide.sftp.fact.path': 'Path',
815
1063
  'conn.guide.sftp.fact.pathHint': 'The first folder is a storage; the root lists the ones you may open.',
816
1064
  'conn.guide.sftp.openssh.connectCaption': 'Connect, put, get',
@@ -834,7 +1082,7 @@ export const en: Record<string, string> = {
834
1082
  'conn.guide.sftp.note.storages': 'The root is not a home directory: it lists the storages you may open, and the first path segment names one.',
835
1083
  'conn.guide.sftp.note.permissions': 'The permission bits your client draws come from your access level here, so a file shown as read-only really is one for you.',
836
1084
  'conn.guide.sftp.note.trash': 'A delete goes to the filex trash, the same as everywhere else, and is recoverable until the retention policy sweeps it.',
837
- 'conn.guide.sftp.note.totp': 'If your account has two-factor sign-in, the password will not work here — SSH has no way to ask for the code. Register a key, or use an API token as the password.',
1085
+ 'conn.guide.sftp.note.totp': 'If your account has two-factor sign-in, the password will not work here — SSH has no way to ask for the code. Register an SSH key, or use an API key as the password.',
838
1086
  'conn.guide.sftp.note.disabled': 'The SFTP endpoint is switched off on this server. These instructions will work once an operator enables it.',
839
1087
  // ── FTPS ─────────────────────────────────────────────────────────
840
1088
  'conn.guide.ftps.summary': 'FTPS is here for the equipment that only ever learned FTP — scan-to-folder printers, EDI counterparties, older lab and industrial software. Always over TLS.',
@@ -845,8 +1093,8 @@ export const en: Record<string, string> = {
845
1093
  'conn.guide.ftps.fact.mode': 'Encryption',
846
1094
  'conn.guide.ftps.fact.modeValue': 'FTPS — explicit TLS (AUTH TLS), required',
847
1095
  'conn.guide.ftps.fact.modeHint': 'Plain FTP is refused before the password is read. Choose "Require explicit FTP over TLS", never "plain FTP" and never "implicit".',
848
- 'conn.guide.ftps.fact.userHint': 'Your short login name, or your e-mail. An API token works as the password too and can be revoked on its own.',
849
- 'conn.guide.ftps.fact.passwordHint': 'Your account password, or an API token. An account with two-factor sign-in must use a token — FTP has no way to ask for the code.',
1096
+ 'conn.guide.ftps.fact.userHint': 'Your username, or your email. An API key works as the password too and can be revoked on its own.',
1097
+ 'conn.guide.ftps.fact.passwordHint': 'Your account password, or an API key. An account with two-factor sign-in must use an API key — FTP has no way to ask for the code.',
850
1098
  'conn.guide.ftps.fact.pasv': 'Passive ports',
851
1099
  'conn.guide.ftps.fact.pasvHint': 'Data connections land here. If your firewall blocks this range the transfer HANGS with no error on either side — that is the classic FTP failure.',
852
1100
  'conn.guide.ftps.filezilla.s1': 'File → Site Manager → New site, Protocol: FTP.',
@@ -859,11 +1107,11 @@ export const en: Record<string, string> = {
859
1107
  'conn.guide.ftps.curl.caption': 'Upload and download',
860
1108
  'conn.guide.ftps.curl.sslReqd': '--ssl-reqd is not optional: it makes curl REQUIRE TLS instead of falling back to plaintext. This server refuses plaintext anyway, but the habit protects you against the servers that do not.',
861
1109
  'conn.guide.ftps.lftp.caption': 'Connect',
862
- 'conn.guide.ftps.lftp.protectData': 'ssl-protect-data encrypts the FILE as well as the login. Without it lftp logs in over TLS and then sends your file in the clear — which is the FTPS misconfiguration people actually ship.',
1110
+ 'conn.guide.ftps.lftp.protectData': 'ssl-protect-data encrypts the FILE as well as the sign-in. Without it lftp signs in over TLS and then sends your file in the clear — which is the FTPS misconfiguration people actually ship.',
863
1111
  'conn.guide.ftps.rclone.useCaption': 'Use it',
864
1112
  'conn.guide.ftps.printer.tab': 'Scanner / printer',
865
1113
  'conn.guide.ftps.printer.s1': 'In the device\'s scan-to-FTP settings: server {host}, port {port}.',
866
- 'conn.guide.ftps.printer.s2': 'User: {user}, password as above. Create an API token for the device rather than using your own password — it can be revoked without changing anything else.',
1114
+ 'conn.guide.ftps.printer.s2': 'User: {user}, password as above. Create an API key for the device rather than using your own password — it can be revoked without changing anything else.',
867
1115
  'conn.guide.ftps.printer.s3': 'Path: {path} — or a folder inside it that you created first. The device cannot create a storage.',
868
1116
  'conn.guide.ftps.printer.s4': 'Turn ON "SSL/TLS" or "FTPS explicit", and PASSIVE mode.',
869
1117
  'conn.guide.ftps.printer.noTLS': 'Plenty of scan-to-FTP firmware cannot do TLS at all. This server will not talk to such a device, and that is deliberate — the alternative is your documents and your password crossing the office network in the clear.',
@@ -934,43 +1182,43 @@ export const en: Record<string, string> = {
934
1182
  'conn.guide.nfs.note.trash': 'A delete goes to the filex trash, the same as everywhere else, and is recoverable until the retention policy sweeps it.',
935
1183
  'conn.guide.nfs.note.revoke': 'Revoking takes effect on the next request, not instantly: NFS has no session to end, so a client with the mount open sees its next operation fail.',
936
1184
  'conn.guide.nfs.note.disabled': 'The NFS endpoint is switched off on this server. These instructions will work once an operator enables it.',
937
- 'conn.tokens.title': 'API tokens',
938
- 'conn.tokens.lead': 'FTPS, WebDAV and filex mount all sign in with a token instead of your account password. Mint one here — it can be revoked on its own, and an account with two-factor sign-in cannot use its password on these at all.',
939
- 'conn.tokens.mint': 'Create token',
1185
+ 'conn.tokens.title': 'API keys',
1186
+ 'conn.tokens.lead': 'FTPS, WebDAV and filex mount all sign in with an API key instead of your account password. Create one here — it can be revoked on its own, and an account with two-factor sign-in cannot use its password on these at all.',
1187
+ 'conn.tokens.mint': 'Create API key',
940
1188
  'conn.tokens.once': 'Copy it now — this is the only time it is shown. Only a hash is stored, so it cannot be shown again.',
941
1189
  'conn.tokens.dismiss': 'I have copied it',
942
- 'conn.tokens.cannotMint': 'This session cannot create tokens. Sign in with your own account to make one.',
943
- 'conn.tokens.empty': 'No tokens yet.',
1190
+ 'conn.tokens.cannotMint': 'This session cannot create API keys. Sign in with your own account to create one.',
1191
+ 'conn.tokens.empty': 'No API keys yet.',
944
1192
  'conn.tokens.neverUsed': 'never',
945
1193
  'conn.tokens.revoke': 'Revoke',
946
1194
  'conn.tokens.confirm': 'Sure?',
947
1195
  'conn.tokens.col.label': 'Name',
948
- 'conn.tokens.col.scopes': 'Can do',
1196
+ 'conn.tokens.col.scopes': 'Permissions',
949
1197
  'conn.tokens.col.used': 'Last used',
950
1198
  'conn.tokens.revokeHint': 'Revoking stops a connection that is already open, not just the next sign-in: an SFTP or FTPS session is cut and a mount stops working within about half a minute.',
951
1199
  'conn.guide.mount.summary': 'filex mount attaches a remote filex server to a folder on this machine, over the same HTTPS the browser uses. It is the only one of these that works from anywhere — no LAN, no extra server, no third-party client.',
952
1200
  'conn.guide.mount.fact.url': 'Server URL',
953
1201
  'conn.guide.mount.fact.urlHint': 'The same address you use in the browser. Nothing else has to be reachable.',
954
- 'conn.guide.mount.fact.token': 'API token',
955
- 'conn.guide.mount.fact.tokenPlaceholder': 'create one under Tokens',
956
- 'conn.guide.mount.fact.tokenHint': 'A token, not your password — it can be revoked on its own, and an account with 2FA cannot use its password here at all.',
1202
+ 'conn.guide.mount.fact.token': 'API key',
1203
+ 'conn.guide.mount.fact.tokenPlaceholder': 'create one under API keys',
1204
+ 'conn.guide.mount.fact.tokenHint': 'An API key, not your password — it can be revoked on its own, and an account with 2FA cannot use its password here at all.',
957
1205
  'conn.guide.mount.fact.remote': 'What to mount',
958
1206
  'conn.guide.mount.fact.remoteHint': 'Leave --remote off to see every storage as a folder, or name one storage — or a folder inside it — to mount just that.',
959
1207
  'conn.guide.mount.linux.mountCaption': 'Mount it',
960
1208
  'conn.guide.mount.linux.umountCaption': 'Unmount it',
961
1209
  'conn.guide.mount.linux.umountWarn': 'Unmount with fusermount, not by killing the process. A mount whose process died without detaching leaves a folder where every ls hangs until somebody runs fusermount by hand.',
962
- 'conn.guide.mount.linux.systemdCaption': 'Mount it at login (systemd user unit)',
1210
+ 'conn.guide.mount.linux.systemdCaption': 'Mount it at sign-in (systemd user unit)',
963
1211
  'conn.guide.mount.win.winfsp': 'Windows needs WinFsp installed once — it is free and open source: https://winfsp.dev. Nothing else, and no administrator rights after that.',
964
1212
  'conn.guide.mount.win.mountCaption': 'Mount it as a drive',
965
1213
  'conn.guide.mount.win.freeLetter': 'Pick a drive letter that is FREE. The letter is created by the mount, not attached to something that already exists, and pointing it at one that is in use fails with a message that does not say so.',
966
1214
  'conn.guide.mount.win.stop': 'Stop it with Ctrl-C in that window. The drive disappears and nothing is left behind on this machine.',
967
- 'conn.guide.mount.mac.unsupported': 'filex mount does not work on macOS. It needs macFUSE, whose licence does not allow a program like filex to install it for you, and whose Go binding needs a C compiler filex deliberately does not require. The command refuses here rather than appearing to work and doing nothing.',
1215
+ 'conn.guide.mount.mac.unsupported': 'filex mount does not work on macOS. It needs macFUSE, whose license does not allow a program like filex to install it for you, and whose Go binding needs a C compiler filex deliberately does not require. The command refuses here rather than appearing to work and doing nothing.',
968
1216
  'conn.guide.mount.mac.alternatives': 'On macOS, use the filex desktop app with folder sync (keeps the files on disk), or SFTP with a client that mounts drives.',
969
1217
  'conn.guide.mount.note.notASync': 'This is not a sync. Nothing is copied to this machine except a small read cache, so a mount opens one file out of a hundred thousand without downloading the rest. If you want the files when you are offline, use folder sync instead.',
970
1218
  'conn.guide.mount.note.reachable': 'It reaches the server through whatever proxy or tunnel the browser goes through, because underneath it is the same API. Your permissions, your storages and — on a multi-tenant server — your tenant come with it.',
971
1219
  'conn.guide.mount.note.wholeFileWrites': 'A file you write through the mount is uploaded when the program closes it, not while it is being written. Editing a very large file in place is slower here than a local disk; copying it in and out is not.',
972
1220
  'conn.guide.mount.note.trash': 'A delete goes to the filex trash, the same as everywhere else, and is recoverable until the retention policy sweeps it.',
973
- 'conn.guide.mount.note.revoke': 'Revoke the token to stop a mount. It stops serving within half a minute, even if the mount is still attached.',
1221
+ 'conn.guide.mount.note.revoke': 'Revoke the API key to stop a mount. It stops serving within half a minute, even if the mount is still attached.',
974
1222
  'storages.driver.ftp': 'FTP / FTPS',
975
1223
  'storages.driver.local': 'Local filesystem',
976
1224
  'storages.driver.s3': 'S3 / Hetzner / MinIO',
@@ -1000,7 +1248,7 @@ export const en: Record<string, string> = {
1000
1248
  'storages.fields.accessKey': 'Access key',
1001
1249
  'storages.fields.basePath': 'Base path',
1002
1250
  'storages.fields.bucket': 'Bucket',
1003
- 'storages.fields.disablePresign': 'Disable presigned URLs',
1251
+ 'storages.fields.disablePresign': 'Stream transfers through filex',
1004
1252
  'storages.fields.endpoint': 'Endpoint',
1005
1253
  'storages.fields.host': 'Host',
1006
1254
  'storages.fields.hostKey': 'Pinned host key',
@@ -1036,6 +1284,10 @@ export const en: Record<string, string> = {
1036
1284
  'sidenav.recent': 'Recent',
1037
1285
  'sidenav.starred': 'Starred',
1038
1286
  'sidenav.shared': 'Shared with me',
1287
+ // The mirror of the row above, and it sits right under it: what came in,
1288
+ // and what went out. ⚠ "My shares", not "Shared by me" — the page lists
1289
+ // LINKS the person minted, not files they granted somebody access to.
1290
+ 'sidenav.myshares': 'My shares',
1039
1291
  'sidenav.trash': 'Trash',
1040
1292
  'sidenav.collapse': 'Collapse navigation',
1041
1293
  'sidenav.expand': 'Expand navigation',
@@ -1071,9 +1323,12 @@ export const en: Record<string, string> = {
1071
1323
  'sidenav.tags.empty': 'No tags yet — add one from a file’s right-click menu.',
1072
1324
  'sidenav.tags.more': 'Show {count} more',
1073
1325
  'sidenav.tags.less': 'Show fewer',
1326
+ 'sidenav.tags.personal': 'Personal',
1327
+ 'sidenav.tags.team': 'Team',
1074
1328
  'empty.tag.title': 'Nothing is tagged “{tag}”',
1075
1329
  'empty.tag.hint': 'Right-click a file and choose Tags to put it here',
1076
1330
  'sidenav.connections': 'Connections',
1331
+ 'sidenav.apps': 'Apps',
1077
1332
  'sidenav.connect': 'How to connect',
1078
1333
  'sidenav.apikeys': 'API keys',
1079
1334
  'overlay.close': 'Close',
@@ -1084,7 +1339,8 @@ export const en: Record<string, string> = {
1084
1339
  'conn.tokens.expiryNever': 'never',
1085
1340
  'conn.tokens.defaultName': 'API key {date}',
1086
1341
  'conn.tokens.namePlaceholder': 'Name — e.g. backup script',
1087
- 'conn.tokens.capNote': 'Your account and its permissions are the ceiling — asking for more than you have is refused, not granted.',
1342
+ 'conn.tokens.capNote': 'Your own access is the ceiling — a permission you do not have is refused, not granted.',
1343
+ 'conn.tokens.scopesRequired': 'Choose at least one permission — an API key is never issued with none.',
1088
1344
  /* === /gezinti:g1 === */
1089
1345
 
1090
1346
  /* === surucu:d1 — the shell (GitHub #14); every profile, no longer a
@@ -1105,8 +1361,12 @@ export const en: Record<string, string> = {
1105
1361
  'newdoc.group.document': 'Documents',
1106
1362
  'newdoc.group.text': 'Text and code',
1107
1363
  'newdoc.group.diagram': 'Diagrams',
1108
- 'newdoc.withheld.onlyoffice': 'Office documents need a document server (OnlyOffice), which is not configured here.',
1109
- 'newdoc.withheld.drawio': 'Diagrams need a drawio service, which is not configured here.',
1364
+ /* ⚠ One name per service, the name its own project uses: draw.io (not
1365
+ "drawio", not "diagrams.net") and ONLYOFFICE — the QA sweep of
1366
+ 2026-09-21 found draw.io called three things on three screens. */
1367
+ 'newdoc.withheld.onlyoffice': 'Office documents need a document server (ONLYOFFICE), which is not set up here.',
1368
+ 'newdoc.withheld.drawio': 'Diagrams need draw.io, which is not set up here.',
1369
+ 'newdoc.withheld_admin': 'Set them up in the admin panel under External services.',
1110
1370
  'newdoc.empty.title': 'No document types are available',
1111
1371
  'newdoc.empty.body': 'This server did not offer any type this app can create.',
1112
1372
  'newdoc.empty.blocked': 'Every type this server can create needs an editor service that is not configured here.',
@@ -1142,7 +1402,7 @@ export const en: Record<string, string> = {
1142
1402
  * what exists nowhere else is below. */
1143
1403
  'home.title': 'Home',
1144
1404
  'home.storages.empty': 'No storages yet',
1145
- 'home.storages.hint': 'A storage is a drive filex reads and writes — an administrator adds them.',
1405
+ 'home.storages.hint': 'A storage is a place filex reads and writes — an administrator adds them.',
1146
1406
  'drive.section.folders': 'Folders',
1147
1407
  'drive.section.files': 'Files',
1148
1408
  'filter.aria': 'Filters',
@@ -1200,6 +1460,10 @@ export const en: Record<string, string> = {
1200
1460
  /* i18n:hardcoded — the convert modal shipped with no keys at all: every
1201
1461
  label was a Turkish literal behind an optional `t` prop nobody passed. */
1202
1462
  'convert.title': 'Convert',
1463
+ /* ⚠ Only an administrator reads this: the iframe converter is retiring,
1464
+ and the Convert app replaces it. The menu never offers both. */
1465
+ 'convert.legacy_admin':
1466
+ 'This is the legacy converter, which is being retired. Install the Convert app (Plugins → Apps) to replace it — once it is installed, this entry goes away.',
1203
1467
  'convert.loading': 'Loading the converter…',
1204
1468
  'convert.done': 'Converted and uploaded to this folder.',
1205
1469
  'convert.close': 'Close',
@@ -1209,6 +1473,12 @@ export const en: Record<string, string> = {
1209
1473
  'convert.unsupported_input': 'This file type has no source format the converter can read.',
1210
1474
  'convert.converting': 'Converting…',
1211
1475
  'convert.convert': 'Convert',
1476
+ /* Never the HTTP layer: a failure here used to show `503 Service
1477
+ Unavailable — {"error":…}` or the converter's own English. */
1478
+ 'convert.unreachable': 'The converter is not answering. Try again later.',
1479
+ 'convert.read_failed': 'The file could not be read for converting.',
1480
+ 'convert.failed': 'The converter could not convert this file to that format.',
1481
+ 'convert.save_failed': 'The file was converted but could not be saved to this folder.',
1212
1482
  /* === gorunum:v1-chrome — the filter row's name box === */
1213
1483
  'filter.find': 'Filter in this folder…',
1214
1484
  /* surucu:d1-scope — the same box where the rows are not a folder's contents.
@@ -1266,6 +1536,7 @@ export const en: Record<string, string> = {
1266
1536
  'ftype.config': 'Configuration',
1267
1537
  'ftype.sheet': 'Spreadsheet',
1268
1538
  'ftype.figma': 'Figma design',
1539
+ 'ftype.drawio': 'draw.io diagram',
1269
1540
  'ftype.design': 'Design file',
1270
1541
  'ftype.image': 'Image',
1271
1542
  'ftype.video': 'Video',
@@ -1305,13 +1576,13 @@ export const en: Record<string, string> = {
1305
1576
  'advsearch.people.name_only':
1306
1577
  'Owner is read off the rows that come back, and only a name search returns rows that carry one.',
1307
1578
  'advsearch.scope.name_only':
1308
- 'Only names and paths here: a content hit reaches the explorer without the drive it came from, so content search is offered on single-storage deployments only.',
1579
+ 'Only names and paths here: a content hit reaches the explorer without the storage it came from, so content search is offered on single-storage deployments only.',
1309
1580
  'advsearch.tags.label': 'Tags',
1310
1581
  'advsearch.tags.placeholder': 'invoice, quarterly report',
1311
1582
  'advsearch.tags.exclude': 'Without tags',
1312
1583
  'advsearch.tags.exclude_placeholder': 'archive',
1313
1584
  'advsearch.tags.hint':
1314
- 'Separate with commas. Tags narrow: several tags means files carrying all of them, and a tag nobody has applied returns nothing.',
1585
+ 'Separate with commas. Tags narrow: several tags means files carrying all of them, and a tag nobody has applied returns nothing. A name matches your personal tag and your team’s tag of that name alike.',
1315
1586
  'advsearch.where.label': 'Folder',
1316
1587
  'advsearch.where.any': 'Anywhere in this storage',
1317
1588
  'advsearch.where.here': 'Only in {folder}',
@@ -1328,12 +1599,12 @@ export const en: Record<string, string> = {
1328
1599
  'advsearch.count.counting': 'Counting…',
1329
1600
  'advsearch.count.result': '{n} matching items',
1330
1601
  'advsearch.count.capped': '{n} matching items in the first page of hits',
1331
- 'advsearch.count.result_one': '1 matching item',
1332
- 'advsearch.count.capped_one': '1 matching item in the first page of hits',
1602
+ 'advsearch.count.result_one': '{n} matching item',
1603
+ 'advsearch.count.capped_one': '{n} matching item in the first page of hits',
1333
1604
  'advsearch.count.error': 'The count could not be run.',
1334
1605
  'advsearch.count.cost':
1335
- 'The count runs this search. Type, owner, date, size and folder are applied to the rows that come back, not by the server.',
1336
- 'advsearch.wire': 'Sent as',
1606
+ 'The count runs this search. Type, owner, date, size and folder narrow the results it finds.',
1607
+ 'advsearch.wire': 'Type this in the search box next time:',
1337
1608
  'advsearch.reset': 'Reset',
1338
1609
  'advsearch.cancel': 'Cancel',
1339
1610
  'advsearch.submit': 'Search',
@@ -1363,17 +1634,17 @@ export const en: Record<string, string> = {
1363
1634
  'access.who.link': 'Anyone with the link can open it.',
1364
1635
  'access.who.pin': 'Anyone with the link and the PIN can open it.',
1365
1636
  'access.who.existing': 'Link sharing is on — {n} links already exist.',
1366
- 'access.who.existing_one': 'Link sharing is on — 1 link already exists.',
1637
+ 'access.who.existing_one': 'Link sharing is on — {n} link already exists.',
1367
1638
  'access.section.link': 'Link options',
1368
1639
  'access.section.people': 'People with access',
1369
1640
  'access.section.drop': 'Request files',
1370
1641
  'access.sum.pin_on': 'PIN on',
1371
1642
  'access.sum.pin_off': 'No PIN',
1372
1643
  'access.sum.people': '{n} people',
1373
- 'access.sum.people_one': '1 person',
1644
+ 'access.sum.people_one': '{n} person',
1374
1645
  'access.sum.people_none': 'No one yet',
1375
1646
  'access.sum.drop': '{n} upload links',
1376
- 'access.sum.drop_one': '1 upload link',
1647
+ 'access.sum.drop_one': '{n} upload link',
1377
1648
  'access.sum.drop_none': 'No upload link',
1378
1649
  /* Ownership — the Owner column and the People chip. "System" is the honest
1379
1650
  word for a row nobody put here through filex (the scanner found it, or it
@@ -1384,7 +1655,7 @@ export const en: Record<string, string> = {
1384
1655
  'owner.unknown': 'Unknown',
1385
1656
  'owner.external': 'Uploaded through an upload link',
1386
1657
  'owner.last_actor': 'Last changed by {who}',
1387
- 'filter.people': 'People',
1658
+ 'filter.people': 'Owner',
1388
1659
  'filter.people.any': 'Anyone',
1389
1660
  'filter.people.me': 'You',
1390
1661
  'filter.people.system': 'System',
@@ -1405,11 +1676,15 @@ export const en: Record<string, string> = {
1405
1676
  'sort.relevance_why':
1406
1677
  'Search results are listed by how well they match. Your sort comes back when you leave the search.',
1407
1678
  /* === tablo:t1 — the table's own configuration ==========================
1408
- Column widths and visibility are a preference about YOUR SCREEN; the view
1409
- a folder opens in is a fact about the FOLDER. Both are set from here, and
1410
- `folder_hint` is the rule in one sentence, written where the person who is
1411
- about to be surprised by it can read it. */
1679
+ Since v0.43 everything about how a folder looks — view, sort AND columns —
1680
+ belongs to the folder it was changed in; a folder nobody changed opens
1681
+ with the person's default (their settings), else the instance's.
1682
+ `folder_hint` is that rule in one sentence, written where the person who
1683
+ is about to be surprised by it can read it. */
1412
1684
  'col.location': 'Location',
1685
+ 'col.deleted': 'Deleted',
1686
+ 'col.deleted_from': 'Deleted from',
1687
+ 'col.remaining': 'Time left',
1413
1688
  'cols.menu': 'Column options',
1414
1689
  'cols.title': 'Columns',
1415
1690
  'cols.resize': 'Resize {col}',
@@ -1418,26 +1693,45 @@ export const en: Record<string, string> = {
1418
1693
  'cols.reset': 'Reset columns',
1419
1694
  'cols.folder_title': 'Folder view',
1420
1695
  'cols.folder_hint':
1421
- 'Folders you set up keep their own view. Your last choice is the default everywhere else.',
1696
+ 'A change you make here stays in this folder. Folders you have never changed open with your default view (Settings → Default folder view).',
1422
1697
  'cols.forget_folder': "Forget this folder's view",
1423
- 'cols.apply_all': 'Apply to all folders ({count} remembered)',
1698
+ 'cols.apply_all': 'Make this my default for all folders ({count} remembered)',
1699
+ /* === tablo:t3 — THE ONE TABLE (components/DataTable.vue) ================
1700
+ Every table in the product speaks these, so a column menu or a pager is
1701
+ never the one English thing on a Turkish page. */
1702
+ 'table.empty': 'Nothing here yet',
1703
+ 'table.prev': 'Previous page',
1704
+ 'table.next': 'Next page',
1705
+ 'table.sort_paged':
1706
+ 'This list is split into pages. Sorting would only re-order the page on screen, so it is off here — narrow the list with the search or filters instead.',
1424
1707
  /* === tablo:t1 — the trash banner ======================================
1425
1708
  ⚠ `retention` is only used when the deployment has TOLD us the number
1426
1709
  (GET /api/admin/protection). Everywhere else `retention_unknown` says the
1427
1710
  same thing without naming a period we cannot verify: a banner that states
1428
1711
  the wrong retention is worse than no banner, because people act on it. */
1429
1712
  'trash.retention': 'Items in the trash are permanently deleted after {days} days.',
1430
- 'trash.retention_one': 'Items in the trash are permanently deleted after 1 day.',
1713
+ 'trash.retention_one': 'Items in the trash are permanently deleted after {days} day.',
1431
1714
  'trash.retention_unknown':
1432
1715
  'Items in the trash are permanently deleted after a retention period set by your administrator.',
1433
1716
  'trash.empty_action': 'Empty trash',
1434
1717
  'trash.empty_confirm_title': 'Empty the trash?',
1435
- 'trash.empty_confirm_body_one': 'This permanently deletes 1 item ({size}). It cannot be undone.',
1718
+ 'trash.empty_confirm_body_one': 'This permanently deletes {count} item ({size}). It cannot be undone.',
1436
1719
  'trash.empty_confirm_body':
1437
1720
  'This permanently deletes {count} items ({size}). It cannot be undone.',
1438
- 'trash.empty_confirm_body_nosize_one': 'This permanently deletes 1 item. It cannot be undone.',
1721
+ 'trash.empty_confirm_body_nosize_one': 'This permanently deletes {count} item. It cannot be undone.',
1439
1722
  'trash.empty_confirm_body_nosize': 'This permanently deletes {count} items. It cannot be undone.',
1440
1723
  'trash.emptied': 'Trash emptied',
1724
+ 'trash.days_remaining': '{n} days',
1725
+ 'trash.days_remaining_one': '{n} day',
1726
+ 'trash.days_remaining_due': 'Due for deletion',
1727
+ 'trash.emptying': 'Emptying the trash… {done} of {total}',
1728
+ 'trash.empty_busy': 'The trash is already being emptied.',
1729
+ 'trash.emptied_partly': 'Trash emptied, but {count} items could not be purged — see the server log.',
1730
+ 'trash.emptied_partly_one': 'Trash emptied, but {count} item could not be purged — see the server log.',
1731
+ 'trash.empty_stopped': 'Emptying the trash stopped: {error}',
1732
+ 'trash.emptying_queued': 'Emptying the trash… waiting for another purge to finish',
1733
+ 'trash.empty_cancelled': 'Emptying the trash was stopped after {count} items; the rest is still in the trash.',
1734
+ 'trash.empty_cancelled_one': 'Emptying the trash was stopped after {count} item; the rest is still in the trash.',
1441
1735
  'empty.trash.hint': 'Files you delete land here first, so you can put them back.',
1442
1736
 
1443
1737
  /* tasi:m1 — the destination picker (modals/DestinationPickerModal.vue) and
@@ -1448,10 +1742,14 @@ export const en: Record<string, string> = {
1448
1742
  'destpicker.confirm.move': 'Move here',
1449
1743
  'destpicker.confirm.copy': 'Copy here',
1450
1744
  'destpicker.confirm.choose': 'Choose this folder',
1745
+ 'destpicker.title.choose_file': 'Choose a file',
1746
+ 'destpicker.confirm.choose_file': 'Choose this file',
1747
+ 'destpicker.pick_a_file': 'Select a file from the list.',
1748
+ 'destpicker.empty_files': 'This folder is empty',
1451
1749
  'destpicker.cancel': 'Cancel',
1452
1750
  'destpicker.up': 'Up one level',
1453
1751
  'destpicker.crumbs': 'Location',
1454
- 'destpicker.drives': 'Drives',
1752
+ 'destpicker.drives': 'Storages',
1455
1753
  'destpicker.loading': 'Loading…',
1456
1754
  'destpicker.empty': 'No subfolders here',
1457
1755
  'destpicker.unreadable': 'This folder cannot be opened.',
@@ -1469,8 +1767,108 @@ export const en: Record<string, string> = {
1469
1767
 
1470
1768
  'toast.archive.preparing': 'Preparing the archive…',
1471
1769
  'toast.archive.started': 'Downloading {name} ({count} files)',
1472
- 'toast.archive.started_one': 'Downloading {name} (1 file)',
1770
+ 'toast.archive.started_one': 'Downloading {name} ({count} file)',
1473
1771
  'toast.archive.empty': 'Nothing to download — the selection contains no readable file.',
1474
1772
  'toast.moved_to': 'Moved to {name}',
1475
1773
  'toast.copied_to': 'Copy to {name} queued',
1774
+
1775
+ /* ── dil:paket — strings that used to be inline `en ? … : …` pairs in
1776
+ FileExplorer / RecentlyOpened, where no language pack could reach them
1777
+ (and where a pack's language fell through to TURKISH). */
1778
+ 'ctx.tags_menu': 'Tags…',
1779
+ 'toast.node_id_copied': 'Node id {id} copied',
1780
+ 'toast.converted_to': 'Converted → {name}',
1781
+ 'toast.read_only_here': 'Read-only here',
1782
+ 'recents.title': 'Recently opened',
1783
+ 'recents.refresh': 'Refresh',
1784
+ 'recents.empty': 'Nothing here yet.',
1785
+ 'recents.loading': 'Loading…',
1786
+ 'recents.close': 'Close',
1787
+ 'tags.title': 'Tags',
1788
+ 'tags.close': 'Close',
1789
+ 'share.ttl.never': 'This link does not expire.',
1790
+ 'share.ttl.until': 'This link is valid until {when}.',
1791
+ 'share.ttl.ceiling': 'Links can be valid for at most {days} days (server setting).',
1792
+ 'share.ttl.ceiling_one': 'Links can be valid for at most {days} day (server setting).',
1793
+ /* ── dil:paket — the share / access dialog (modals/PermissionsModal.vue). These were ~90 inline (tr, en) pairs: no language pack could reach them, and under any third language the dialog spoke TURKISH. access.ui.emails_comma_separated / access.ui.send / access.ui.mail_not_set_up are the mail-row lines another branch rewrote in the same release, converted at the v0.43.0 merge. */
1794
+ 'access.ui.level_viewer': 'Viewer',
1795
+ 'access.ui.level_viewer_desc': 'view + download',
1796
+ 'access.ui.level_editor': 'Editor',
1797
+ 'access.ui.level_editor_desc': 'read + write + delete',
1798
+ 'access.ui.level_owner': 'Owner',
1799
+ 'access.ui.level_owner_desc': 'edit + manage access',
1800
+ 'access.ui.never': 'Never',
1801
+ 'access.ui.days': '{days} days',
1802
+ 'access.ui.days_one': '{days} day',
1803
+ 'access.ui.unlimited': 'Unlimited',
1804
+ 'access.ui.downloads': '{count} downloads',
1805
+ 'access.ui.downloads_one': '{count} download',
1806
+ 'access.ui.mail_sent': 'Email sent ✓ ({sent})',
1807
+ 'access.ui.mail_partial': '{sent} sent, {failed} failed',
1808
+ 'access.ui.valid_for': 'This link is valid for {days} days.',
1809
+ 'access.ui.valid_for_one': 'This link is valid for {days} day.',
1810
+ 'access.ui.mail_title_file': '{name} has been shared with you',
1811
+ 'access.ui.mail_title_folder': '{name} has been shared with you',
1812
+ 'access.ui.mail_hello': 'Hello,',
1813
+ 'access.ui.mail_shared_file': 'A file has been shared with you:',
1814
+ 'access.ui.mail_shared_folder': 'A folder has been shared with you:',
1815
+ 'access.ui.mail_folder': 'Folder: {name}',
1816
+ 'access.ui.mail_file': 'File: {name}',
1817
+ 'access.ui.mail_size': 'Size: {size}',
1818
+ 'access.ui.mail_download_here': 'Download it here:',
1819
+ 'access.ui.mail_pin': 'PIN: {pin}',
1820
+ 'access.ui.drop_title': 'You\'ve been asked to add files to {folder}',
1821
+ 'access.ui.drop_invited': 'You\'ve been invited to upload files.',
1822
+ 'access.ui.drop_limit': 'Limit: up to {files} file(s), {mb} MB per file.',
1823
+ 'access.ui.drop_types': 'Allowed types: {types}',
1824
+ 'access.ui.drop_types_all': 'Allowed types: all',
1825
+ 'access.ui.drop_upload_here': 'Upload your files here:',
1826
+ 'access.ui.inherited_from': 'Inherited from',
1827
+ 'access.ui.access_granted': 'Access granted.',
1828
+ 'access.ui.enter_a_valid_email': 'Enter a valid email.',
1829
+ 'access.ui.user_created_invite_emailed': 'User created, invite emailed.',
1830
+ 'access.ui.user_created_share_the_temp_password': 'User created. Share the temp password.',
1831
+ 'access.ui.smtp_not_set_up_verified_share_the_link': 'SMTP not set up/verified — share the link manually.',
1832
+ 'access.ui.send_failed_temporary_please_retry': 'Send failed (temporary) — please retry.',
1833
+ 'access.ui.server_limit_applied': '(server limit applied)',
1834
+ 'access.ui.protect_with_a_pin': 'Protect with a PIN',
1835
+ 'access.ui.expiry': 'Expiry',
1836
+ 'access.ui.download_limit': 'Download limit',
1837
+ 'access.ui.create_link': 'Create link',
1838
+ 'access.ui.replace_link': 'Replace the link with these settings',
1839
+ 'access.ui.replace_link_hint': 'The address changes: the current link stops working.',
1840
+ 'access.ui.create_then_send': 'Create a link, then it will be sent to {email}.',
1841
+ 'access.ui.command_line': 'Command line',
1842
+ 'access.ui.share': 'Share',
1843
+ 'access.ui.existing_links': 'Existing links',
1844
+ 'access.ui.rbac_off_here': 'Per-item access control is off on this storage — everyone opens it according to their role, so people added here would change nothing. Turn it on in the admin panel under Storages (this storage → Per-item access control).',
1845
+ 'access.ui.other_links': 'Other links',
1846
+ 'access.ui.revoke': 'Revoke',
1847
+ 'access.ui.loading': 'Loading…',
1848
+ 'access.ui.name_or_email': 'Name or email',
1849
+ 'access.ui.add': 'Add',
1850
+ 'access.ui.no_account_for_this_email_what_next': 'No account for this email — what next?',
1851
+ 'access.ui.user': 'User',
1852
+ 'access.ui.viewer': 'Viewer',
1853
+ 'access.ui.create_user_grant': 'Create user + grant',
1854
+ 'access.ui.just_send_a_share_link': 'Just send a share link →',
1855
+ 'access.ui.temp_password': 'Temp password:',
1856
+ 'access.ui.people_with_access': 'People with access',
1857
+ 'access.ui.not_shared_with_anyone_yet': 'Not shared with anyone yet.',
1858
+ 'access.ui.remove': 'Remove',
1859
+ 'access.ui.a_public_link_that_lets_anyone_upload_fi': 'A public link that lets anyone UPLOAD files into this folder. Uploaders never see the folder\'s existing files.',
1860
+ 'access.ui.hide_upload_limits': 'Hide upload limits',
1861
+ 'access.ui.upload_limits': 'Upload limits',
1862
+ 'access.ui.max_files': 'Max files',
1863
+ 'access.ui.mb_file': 'MB / file',
1864
+ 'access.ui.allowed_types': 'Allowed types',
1865
+ 'access.ui.all_e_g_pdf_jpg': 'all (e.g. pdf, jpg)',
1866
+ 'access.ui.ask_uploader_name': 'Ask uploader name',
1867
+ 'access.ui.emails_comma_separated': 'email(s) — comma separated',
1868
+ 'access.ui.send': 'Send',
1869
+ 'access.ui.mail_not_set_up': 'Email is not set up — set it up in the admin panel under Settings (Email / SMTP).',
1870
+ /* ── dil:paket — accessible names that were English literals */
1871
+ 'breadcrumb.aria': 'Breadcrumb',
1872
+ 'e2e.recovery_key_aria': 'Recovery key',
1873
+ 'modal.close': 'Close',
1476
1874
  };