Compare commits

...

237 Commits

Author SHA1 Message Date
Dan Brown
07e45a20e5 Updated version and assets for release v24.10.3 2024-11-29 13:50:41 +00:00
Dan Brown
14056c69e6 Updated version and assets for release v24.10.2 2024-11-29 13:47:24 +00:00
Dan Brown
fb9c840c46 Merge branch 'development' into release 2024-11-29 13:47:08 +00:00
Dan Brown
94165cc18f Updated translator & dependency attribution before release v24.10.2 2024-11-29 13:46:37 +00:00
Dan Brown
f5ecd51461 Updated translations with latest Crowdin changes (#5331) 2024-11-29 13:40:09 +00:00
Dan Brown
e9f906ce56 Attachments: Fixed full range request handling
We were not responsing with a range request, where the requested range
was for the full extent of content. This changes things to always
provide a range request, even for the full range.

Change made since our existing logic could cause problems in chromium
browsers.

Elseif statement removed as its was likley redundant based upon other
existing checks.
This also changes responses for requested ranges beyond content, but I
think that's technically correct looking at the spec (416 are for when
there are no overlapping request/response ranges at all).

Updated tests to cover.
For #5342
2024-11-29 13:19:55 +00:00
Dan Brown
4630f07282 Code: Set base codemirror line height
Prevents difference in line height between light/dark mode.
For #5146
2024-11-29 12:57:53 +00:00
Dan Brown
978acecdcf Merge branch 'oidc-content-type-issue' into development 2024-11-28 16:58:55 +00:00
Dan Brown
bc1f1d92e5 OIDC: Added extra userinfo content-type normalisation and test
During review of #5337
2024-11-28 16:58:06 +00:00
Dan Brown
415cd6a360 Includes: Workaround for PHP 8.3.14 bug
Changed DOMText creation to be done via document so its document
reference is correct to avoid a bug in PHP 8.3.14.
Ref: https://github.com/php/php-src/issues/16967

Fixes #5341
2024-11-28 16:30:59 +00:00
Dan Brown
68ce340741 Depenencies: Updated PHP packages 2024-11-28 16:25:01 +00:00
Wes Biggs
17f7afe12d Updates the OIDC userinfo endpoint request to allow for a Content-Type response header with optional parameters, like application/json; charset=utf-8. This was causing an issue when integrating with [node-oidc-provider](https://github.com/panva/node-oidc-provider). 2024-11-26 11:21:20 -06:00
Dan Brown
5fba4a5399 Updated version and assets for release v24.10.2 2024-11-13 12:03:15 +00:00
Dan Brown
c0b377050e Merge branch 'development' into release 2024-11-13 12:02:30 +00:00
Dan Brown
306b8774c2 Updated translations with latest Crowdin changes (#5317)
* New translations common.php (Ukrainian)

* New translations entities.php (Ukrainian)

* New translations errors.php (Ukrainian)

* New translations activities.php (Czech)

* New translations entities.php (Czech)
2024-11-13 11:59:03 +00:00
Dan Brown
c40ab4147e Dependencies: Updated composer packages 2024-11-13 11:39:04 +00:00
Dan Brown
f3efb6441d Updated version and assets for release v24.10.1 2024-11-08 13:53:06 +00:00
Dan Brown
0cf313a21e Merge branch 'development' into release 2024-11-08 13:52:37 +00:00
Dan Brown
ac27e18933 Languages: Added Turkmen to locale manager 2024-11-08 13:46:57 +00:00
Dan Brown
e5a6ccc4d4 Translators: Updated before patch release 2024-11-08 13:31:21 +00:00
Dan Brown
e42cdbe8e0 Updated translations with latest Crowdin changes (#5250) 2024-11-08 13:29:21 +00:00
Dan Brown
a6ba8dd68f Testing: Improved reliability
- Added extra column/value check for page revision test for accuracy.
- Changed search sort test to use more reliable values.
  - Change due to database seeding somtimes generating values that
    proceeded the test value, expected to be first, in sort results.
2024-11-08 11:35:18 +00:00
Dan Brown
7017a1cae5 Update URL Command: Added revisions table support
For #5292
Added test to cover.
2024-11-08 11:22:30 +00:00
Dan Brown
8120278b8c PHP Deps: Bumped up minor versions 2024-11-08 10:41:25 +00:00
Dan Brown
73babcbfe3 Merge pull request #5312 from BookStackApp/system_cli_update
System CLI update
2024-11-07 17:22:08 +00:00
Dan Brown
45189d9517 System CLI: Updated to 126de5599c state 2024-11-07 17:10:35 +00:00
Dan Brown
e4ca3bf132 Merge pull request #5291 from LordSimal/development
fix tests namespace definition
2024-10-27 09:54:11 +00:00
Kevin Pfeifer
7aaf866064 fix tests namespace definition 2024-10-26 13:24:49 +02:00
Dan Brown
6f1c54d018 Users: Changed name validation to min:1 instead of 2
Would cause scenarios where users could be created with 1 char, but then
fail to update due to validation differences.
Added test to cover.
For #5263
2024-10-15 11:07:41 +01:00
Dan Brown
4dc75bad05 Settings: Added test to cover setting category by view 2024-10-11 13:33:07 +01:00
Lachlan Tripolone
a3d0f7478f Move settings category layouts into their own view folder 2024-10-11 10:42:48 +11:00
Lachlan Tripolone
b9b5003239 Refactor SettingController to validate categies by existing view files 2024-10-11 10:40:38 +11:00
Dan Brown
26aadffb20 Updated version and assets for release v24.10 2024-10-09 10:48:34 +01:00
Dan Brown
a5f48e3202 Merge branch 'development' into release 2024-10-09 10:46:07 +01:00
Dan Brown
a58102d6ef Attribution: Updated translator & license files before v24.10 2024-10-09 10:26:07 +01:00
Dan Brown
65453bd94e Updated translations with latest Crowdin changes (#5188) 2024-10-09 10:21:55 +01:00
Dan Brown
d22413b931 JS: Converted/updated translation code to TS, fixed some comment counts
- Migrated translation service to TS, stripping a lot of now unused code
  along the way.
- Added test to cover translation service.
- Fixed some comment count issues, where it was not showing correct
  value. or updating, on comment create or delete.
2024-10-07 22:55:10 +01:00
Dan Brown
8b9bcc1768 Search: Fixed last commented filter when using table prefixes 2024-10-05 15:20:04 +01:00
Dan Brown
51287d545b Searching: Fixed some form search issues
- Form was not retaining certain filters
- Form request handling of entity type set wrong filter name
Added test to cover.
2024-10-05 14:49:30 +01:00
Dan Brown
c314a60a16 WYSIWYG: Code & table fixes
- Fixed new code block insertion to remove selection area instead of
  just adding after.
- Added default table column widths to not be collapsed
- Updated table dom export to not duplicate colgroups.
2024-10-05 12:42:47 +01:00
Dan Brown
9b2520aa0c WYSIWYG: Fixed list indenting selection & display bugs
- Fixed selection breaking on multiple indent changes
- Fixed multi-indent showing numbers on empty child list until the nodes
  are fully re-rendered.
2024-10-04 15:11:09 +01:00
Dan Brown
346b88ae43 JS: Converted a few extra services to TS 2024-10-04 14:36:20 +01:00
Dan Brown
2766c76491 TinyMCE: Updated version from 6.8.3 to 6.8.4 2024-10-04 12:46:22 +01:00
Dan Brown
be6529d0a1 New WYSIWYG: Added mac shortcut support 2024-10-04 12:41:13 +01:00
Dan Brown
b1a3ea1aa4 Languages: Enabled Welsh option 2024-10-04 11:02:17 +01:00
Dan Brown
6646dcc24d Merge pull request #5239 from BookStackApp/search_negation
Search term negation
2024-10-03 19:52:06 +01:00
Dan Brown
966ff91386 Search: Prevented negated terms filling in UI inputs
Added test to cover.
2024-10-03 19:40:11 +01:00
Dan Brown
cd84d08157 Search: Added exact/filter/tag term negation support 2024-10-03 19:27:03 +01:00
Dan Brown
93c677a6a9 Searching: Added negation support to UI and term handling
Updated/added tests to cover.
Support for actual search queries still remains.
2024-10-03 15:59:50 +01:00
Dan Brown
177cfd72bf Search: Added structure for search term inputs
Sets things up to allow more complex terms ready to handle negation.
2024-10-02 17:31:45 +01:00
Dan Brown
34ade50181 Base layout: Changed main app script to be module loaded
Prevents polluting global scope with variables since we're using the
module format bundler in esbuild.
Also cleaned up unused yields.
Fixed bad reference in our tinymce fixes.

For #5232
2024-10-01 10:37:31 +01:00
Dan Brown
e65655594f Merge branch 'feature/opensearch' into development 2024-09-30 17:21:51 +01:00
Dan Brown
514db60617 Tests: Categorised up meta tests
Extracted robots.txt tests into its own file to fit into new folder.
Also tweaked open search tests a tad to specifically check long app
names.
2024-09-30 17:07:53 +01:00
Dan Brown
8bc6e75319 Code Blocks: Added SAS and R language options
For #5206
2024-09-30 16:47:55 +01:00
Maximilian Walter
2f74cfb42c Add test for OpenSearch endpoint 2024-09-30 17:45:20 +02:00
Maximilian Walter
1302e3c959 Add missing XML declaration to OpenSearch endpoint 2024-09-30 17:45:20 +02:00
Maximilian Walter
a5b031f906 Translatable description for OpenSearch XML 2024-09-30 17:45:20 +02:00
Dan Brown
f583354748 Maintenance: Removed stray dd from last commit 2024-09-29 16:50:48 +01:00
Dan Brown
d12e8ec923 Users: Improved user response for failed invite sending
Added specific handling to show relevant error message when user
creation fails due to invite sending errors, while also returning user
to the form with previous input.
Includes test to cover.

For #5195
2024-09-29 16:41:18 +01:00
Dan Brown
89f84c9a95 Pages: Updated editor field to always be set
- Migration for setting on existing pages
- Added test to cover simple new page scenario

For #5117
2024-09-29 14:36:41 +01:00
Dan Brown
6103a22feb Exports: Made pdf command timeout configurable
Added test to cover.
For #5119
2024-09-27 16:33:58 +01:00
Dan Brown
42264f402d CSS: Fixed floating search icon on mobile
Also updated styles to use logical elements instead of conditional rules
for altered search boxes.
Related to #2504
2024-09-27 16:02:13 +01:00
Dan Brown
abda9bc00a PHP Dependancies: Updated packages pending major version changes
Closes #5222
2024-09-27 14:21:12 +01:00
Dan Brown
eec639d84e Maintenance: Fixed js lint and SCSS build warnings 2024-09-27 13:57:39 +01:00
Dan Brown
56b9107c6b Dependancies: Updated php & JS deps, updated license lists
Fixed issue now picked up by newer TS version
2024-09-27 12:29:19 +01:00
Dan Brown
b35b62d59f Merge branch 'lexical' into development 2024-09-27 12:04:01 +01:00
Dan Brown
1b9310e766 Meta: Added lexical licensing info and added TS/JS CI testing 2024-09-27 10:45:48 +01:00
Dan Brown
a62d8381be Lexical: Updated toolbar & text node exporting
- Updated toolbar to match existing editor, including dynamic RTL/LTR
  controls.
- Updated text node handling to not include spans and extra classes when
  not needed. Added & update tests to cover.
2024-09-23 17:36:16 +01:00
Dan Brown
8b32e6c15a Page Editors: Added switching/options for new lexical editor 2024-09-22 20:06:55 +01:00
Dan Brown
c8ccb2bac7 Lexical: Range of fixes
- Prevented ui shortcuts running in editor
- Added form modal closing on submit
- Fixed ability to escape lists via enter on empty last item
2024-09-22 16:15:02 +01:00
Dan Brown
ef3de1050f Lexical: Added UI translation support 2024-09-22 12:29:06 +01:00
Dan Brown
2add15bd72 Lexical: Added direction support to extra blocks
Also removed duplicated dir functionality that remained in core.
2024-09-22 12:07:24 +01:00
Dan Brown
e6edd9340e Lexical: Added alignment detoggle, fixed inital focus area 2024-09-21 17:02:54 +01:00
Dan Brown
654a7a5d03 Lexical: Removed reconciler level direction handling
- Updated tests to consider changes
2024-09-21 13:00:16 +01:00
Dan Brown
dba8ab947f Lexical: Finished conversion/update of test files 2024-09-20 15:31:19 +01:00
Dan Brown
787e06e3d8 Lexical: Adapted a range of further existing tests 2024-09-20 13:05:29 +01:00
Dan Brown
ccd486f2a9 Lexical: Got a range of Editor tests working 2024-09-18 17:31:51 +01:00
Dan Brown
22d078b47f Lexical: Imported core lexical libs
Imported at 0.17.1, Modified to work in-app.
Added & configured test dependancies.
Tests need to be altered to avoid using non-included deps including
react dependancies.
2024-09-18 13:43:39 +01:00
Dan Brown
03490d6597 Lexical: Added RTL/LTR actions
Kinda useless though due to Lexical reconciler :(
2024-09-16 12:29:46 +01:00
Dan Brown
5f46d71af0 Lexical: Fixed a range of issues in RTL mode 2024-09-15 16:10:46 +01:00
Maximilian Walter
4f890c431c Limit short-name for OpenSearch XML to 16 characters
The specification does not allow more than 16 characters.
2024-09-14 15:31:56 +02:00
Maximilian Walter
c110a97d8a Remove unofficial method-attribute from OpenSearch-XML 2024-09-14 15:24:42 +02:00
Dan Brown
6872eb802c Lexical: Altered keyboard handling to indicant handled state 2024-09-13 16:05:55 +01:00
Dan Brown
662110c269 Lexical: Custom list nesting support
Added list nesting support to allow li > ul style nesting which lexical
didn't do by default.
Adds tab handling for inset/outset controls.
Will be a range of edge-case bugs to squash during testing.
2024-09-13 15:50:42 +01:00
Dan Brown
5083188ed8 Lexical: Added block indenting capability
Needed a custom implementation due to hardcoded defaults for Lexical
default indenting.
2024-09-10 15:55:46 +01:00
Dan Brown
2036438203 Lexical: Added single node enter handling
Also updated media to be an inline element to align with old editor
behaviour.
2024-09-10 12:14:26 +01:00
Maximilian Walter
476c2be5a6 Add XML for OpenSearch 2024-09-09 22:54:33 +02:00
Dan Brown
ced66f1671 Lexical: Added single node backspace/delete support 2024-09-09 18:33:54 +01:00
Dan Brown
fb49371c6b Lexical: Refined editor UI
- Cleaned up dropdown lists to look integrated
- Added icons for color picker clear and menu list items
2024-09-09 14:06:41 +01:00
Dan Brown
fd07aa0f05 Lexical: Further fixes
- Improved node resizer positioning to be more accurate
- Fixed drop handling not running within editor margin space
- Made media dom update smarter to reduce reloads
- Fixed media alignment, broken due to added wrapper
2024-09-09 12:28:01 +01:00
Dan Brown
16518a4f89 Lexical: Range of bug fixes, Updated lexical version
- Updated selection change detection to be more accurate
- Added UI refresh for extra actions
- Fixed remove link deleting contents
2024-09-08 15:54:59 +01:00
Dan Brown
bed2c29a33 Lexical: Added media resize support via drag handles 2024-09-08 13:37:13 +01:00
Dan Brown
e5b6d28bca Lexical: Revamped image node resize method
Changed from using a decorator to using a helper that watches for image
selections to then display a resize helper.
Also changes resizer to use a ghost and apply changes on end instead of
continuosly during resize.
2024-09-07 18:39:58 +01:00
Dan Brown
1c9afcb84e Lexical: Added some level of img/media alignment 2024-09-06 14:07:10 +01:00
Dan Brown
b0dda6e6a7 Updated version and assets for release v24.05.4 2024-08-29 16:04:51 +01:00
Dan Brown
d4025d95e7 Merge branch 'development' into release 2024-08-29 16:04:37 +01:00
Dan Brown
3a058a6e34 Merge branch 'development' of github.com:BookStackApp/BookStack into development 2024-08-29 15:28:52 +01:00
Dan Brown
aac7d564c8 Updated translations with latest Crowdin changes (#5118) 2024-08-29 15:08:27 +01:00
Dan Brown
9aa3442a17 API: Fixed lacking permission enforcement on book contents 2024-08-29 14:43:21 +01:00
Dan Brown
c68d154f0f LDAP: Updated tests for recursive group changes 2024-08-28 21:16:18 +01:00
Dan Brown
1b4ed69f41 LDAP: Updated recursive group search to query by DN
Added test to cover, added pre-change.
Need to test post-changes and fix tests.
2024-08-28 15:39:05 +01:00
Dan Brown
8cef998f49 RTL: Fixed lacking task list RTL support
Added with fallback to old LTR styles.
For #5134
2024-08-27 14:13:33 +01:00
Dan Brown
90d1223acd Styles: Added max-width for iframes in content
For #5130
2024-08-27 13:32:16 +01:00
Dan Brown
1f2506221a API: Updated docs with consistent types, fixed users response example
For #5178 and #5183
2024-08-27 12:23:36 +01:00
Dan Brown
9f68ca5358 Dependancies: Updated PHP and JS packages 2024-08-26 11:49:02 +01:00
Dan Brown
1ebb0f8c93 Lexical: Added table column cut/copy/paste support 2024-08-22 13:28:30 +01:00
Dan Brown
8a13a9df80 Lexical: Improved table row copy/paste
Added safeguarding/matching of source/target sizes to prevent broken
tables.
2024-08-22 10:08:08 +01:00
Dan Brown
ddf5f2543c Lexical: Added drop/paste image handling 2024-08-21 12:59:45 +01:00
Dan Brown
dbb2fe3e59 Lexical: Finished off baseline shortcut implementation 2024-08-20 14:54:53 +01:00
Dan Brown
aa1fac62d5 Lexical: Started adding editor shortcuts 2024-08-20 13:07:33 +01:00
Dan Brown
111a313d51 Lexical: Added custom alignment handling for blocks
To align with pre-existing use of alignment classes.
2024-08-18 16:51:08 +01:00
Dan Brown
0039f893cc Lexical: Integrated diagram manager, added menu split button 2024-08-17 10:48:34 +01:00
Dan Brown
ad6b26ba97 Lexical: Added basic URL field header option list
May show bad option label names on chrome/safari.
This was an easy first pass without loads of extra custom UI since we're
using native datalists.
2024-08-16 12:29:40 +01:00
Dan Brown
1ef4044419 Lexical: Connected link selector to link form 2024-08-16 11:22:12 +01:00
Dan Brown
accf2565a0 Lexical: Integrated image manager to image button/form 2024-08-13 19:36:18 +01:00
Dan Brown
ec965f28c0 Lexical: Added id support for all main block types 2024-08-11 16:08:51 +01:00
Dan Brown
ebf95f637a Lexical: Wired table properties, and other buttons 2024-08-10 13:14:55 +01:00
Dan Brown
abbfd42a6c Lexical: Kinda made row copy/paste work 2024-08-09 21:58:45 +01:00
Dan Brown
db4208a7eb Lexical: Linked row properties form up 2024-08-09 12:42:04 +01:00
Dan Brown
da54e1d87c Lexical: Added cell width fetching, Created custom row node 2024-08-09 11:24:25 +01:00
Dan Brown
e8532ef4de Lexical: Added merge cell logic 2024-08-07 20:32:54 +01:00
Dan Brown
fa6d66db49 Readme: Updated sponsor image links to use website 2024-08-07 10:53:20 +01:00
Alexander Wilms
6604e7365f Update sponsor image URLs in readme 2024-08-06 23:30:05 +00:00
Dan Brown
fcc1c2968d Lexical: Added table cell node import logic 2024-08-06 09:36:37 +01:00
Dan Brown
b3d3b14f79 Lexical: Finished off core cell properties functionality 2024-08-05 18:49:17 +01:00
Dan Brown
8939f310db Lexical: Started linking up cell properties form 2024-08-05 15:08:52 +01:00
Dan Brown
efec752985 Lexical: Split helpers to utils, refactored files 2024-08-03 18:14:01 +01:00
Dan Brown
e94ad78ea7 Lexical: Completed out table menu elements, logic pending 2024-08-03 18:01:54 +01:00
Dan Brown
a27a325af7 Lexical: Started on table actions
Started building table cell form/actions
2024-08-02 15:28:54 +01:00
Dan Brown
6b06d490c5 Lexical: Started table menu options
Updated UI elements to handle new scenarios needed in more complex table
menu
2024-08-02 11:16:54 +01:00
Dan Brown
13f8f39dd5 Lexical: Updated task list to use/support old format 2024-07-30 14:42:19 +01:00
Dan Brown
fe05cff64f Lexical: Linked up change/draft management 2024-07-29 21:43:20 +01:00
Dan Brown
d86837ac07 Lexical: Got working with attachment insert/drop 2024-07-29 21:14:42 +01:00
Dan Brown
9a7edc6e52 Lexical: Started drop handling, handled templates 2024-07-29 15:27:41 +01:00
Dan Brown
ce8c9dd079 Lexical: Added form complex/tab ui support 2024-07-28 12:48:58 +01:00
Dan Brown
c8f6b7e0d6 Lexical: Got media node core work & form done 2024-07-27 17:25:30 +01:00
Dan Brown
f284d31861 Lexical: Started media node support 2024-07-25 16:25:08 +01:00
Dan Brown
76b0d2d5d8 Lexical: Added common events support 2024-07-23 15:35:18 +01:00
Dan Brown
2cab778f19 Lexical: Improved table resize bars
Added scoll & page resize handling.
Added cropping/limiting to edit area.
2024-07-23 12:45:58 +01:00
Dan Brown
c31f8eb2e0 Readme: Added route4me sponsorship 2024-07-22 16:51:56 +01:00
Dan Brown
b618287585 Lexical: Added table toolbar, organised button code 2024-07-21 15:11:24 +01:00
Dan Brown
63f4b42453 Lexical: Added toolbar scroll/resize handling
Also added smarter above/below positioning to respond if toolbar would
be off the bottom of the editor, and added hide/show when they'd go
outside editor scroll bounds.
2024-07-19 18:12:51 +01:00
Dan Brown
c7c0df0964 Lexical: Finished up core drawing insert/editing
Added new options that sits on the context, for things needed but not
for the core editor, which are defined out of the editor (drawio URL,
error message text, pageId etc...)
2024-07-19 12:09:41 +01:00
Dan Brown
fb87fb5750 JS: Converted http service to ts 2024-07-18 15:13:14 +01:00
Dan Brown
634b0aaa07 Lexical: Started converting drawio to TS
Converted events service to TS as part of this.
2024-07-18 11:19:11 +01:00
Dan Brown
5002a89754 Lexical: Standardised helper function format 2024-07-17 16:45:57 +01:00
Dan Brown
b367490edc Lexical: Added list support, started todo 2024-07-17 16:38:20 +01:00
Dan Brown
e145f21512 Dev compose: Set image versions, removed unsupported mysql flag
Quick local test performed, ran a working instance.
For #5124
2024-07-17 11:13:39 +01:00
Dan Brown
ea4c50c2c2 Lexical: Added code block selection & edit features
Also added extra lifecycle handling for decorators to things can be
properly cleaned up after node destruction.
2024-07-16 16:36:08 +01:00
Dan Brown
d6021f4d22 Updated version and assets for release v24.05.3 2024-07-14 17:14:21 +01:00
Dan Brown
b9a3290731 Merge branch 'development' into release 2024-07-14 17:13:10 +01:00
Dan Brown
47ac0d5c3e Updated translator & dependency attribution before release v24.05.3 2024-07-14 17:09:41 +01:00
Dan Brown
75f225d6dc Updated translations with latest Crowdin changes (#5065) 2024-07-14 16:39:50 +01:00
Dan Brown
adb7bf7016 Codemirror: Enabled non-standard self-closing tags
For #5078
2024-07-14 16:36:36 +01:00
Dan Brown
897bb338f9 CSP: Updated handling of drawio URL to consider port
Previously if a custom port was used in the DRAWIO option it would not
be considered in the CSP handling, which would block loading.

Added test to cover.
For #5107
2024-07-14 16:06:18 +01:00
Dan Brown
767699a066 OIDC: Fixed incorrect detection of group detail population
An empty (but valid formed) groups list provided via the OIDC ID token
would be considered as a lacking detail, and therefore trigger a lookup
to the userinfo endpoint in an attempt to get that information.

This fixes this to properly distinguish between not-provided and empty
state, to avoid userinfo where provided as valid but empty.

Includes test to cover.
For #5101
2024-07-14 14:21:16 +01:00
Dan Brown
7161f22706 Dependancies: Updated composer & npm deps 2024-07-14 13:55:46 +01:00
Dan Brown
ddec8097b7 Merge pull request #5096 from DanielGordonIT/normalize-file-extensions
Wraps file extension comparison components in strtolower()
2024-07-14 13:51:55 +01:00
Dan Brown
95c3cc5c00 Styles: Improved callout RTL support
Will now adapt using logical styles where supported, will fallbacks
to old fixed LTR positioning where not supported.
For #5104
2024-07-14 12:21:07 +01:00
Dan Brown
60c53705ca Merge pull request #5069 from mueller-contria/5068-allowed_iframe_sources_in_phpunit_xml
Add ALLOWED_IFRAME_SOURCES to phpunit.xml
2024-07-14 12:06:17 +01:00
Dan Brown
51d8044a54 Lexical: Added initial form/modal styles 2024-07-09 20:49:47 +01:00
Dan Brown
ce697ab0f5 Readme: Added sponsor, removed road map section
Road map section was very much outdated and redundant so removing to
avoid confusion.
2024-07-09 14:37:29 +01:00
DanielGordonIT
ca310966b2 Actually add the test this time 2024-07-05 03:59:49 +00:00
DanielGordonIT
25f92ce584 Add test to verify different case on extensions works 2024-07-04 19:48:12 -04:00
Dan Brown
2c96af9aea Lexical: Worked on toolbar styling, got format submenu working 2024-07-04 16:16:16 +01:00
Dan Brown
04c7e680fd Lexical: Linked up saving logic of editor via interface 2024-07-04 13:09:53 +01:00
DanielGordonIT
9b0ef85f77 Wraps file extension comparison components in strtolower()
This avoids the issue where replacing file.PNG with newfile.png fails due to "PNG" not being equal to "png"
2024-07-03 15:50:25 -04:00
Dan Brown
a8f1160743 JS: Converted come common services to typescript 2024-07-03 11:00:57 +01:00
Dan Brown
feca1f0502 Lexical: Started diagram support 2024-07-03 10:28:04 +01:00
Dan Brown
d0a5a5ef37 Lexical: Linked code block to editor, added button 2024-07-02 17:34:03 +01:00
Dan Brown
97f570a4ee Lexical: Started code block node implementation 2024-07-02 14:46:30 +01:00
Dan Brown
9ebbf7ce94 Lexical: Started loading real content, Improved html loading
Added more styling/layout for buttons and main content area
2024-07-01 15:10:22 +01:00
Dan Brown
c2ecbf071f Lexical: Added tracked container, added fullscreen action
Changed how the editor is loaded in, so it now creates its own DOM, and
content is passed via creation function, to be better self-contained.
2024-07-01 10:44:23 +01:00
Dan Brown
b1c489090e Lexical: Added context toolbar placement, added link toolbar
Also added some basic context toolbar styling
2024-06-30 19:52:09 +01:00
Dan Brown
c9a03c5b01 Lexical: Added base context toolbar logic 2024-06-30 12:13:13 +01:00
Dan Brown
517c578a5f Lexical: Reorganised some logic into manager 2024-06-30 10:31:39 +01:00
Dan Brown
14837e34fb Readme: Added sponsor practinet 2024-06-28 22:28:06 +01:00
Dan Brown
f10ec3271a Lexical: Added overflow container 2024-06-27 16:28:06 +01:00
Dan Brown
4e2820d6e3 Lexical: Added horizontal rule node 2024-06-27 15:48:06 +01:00
Dan Brown
72a0e081ca Lexical: Completed initial table cell resize handle logic 2024-06-26 17:22:00 +01:00
Dan Brown
b1130cb1c3 Lexical: Linked up table resize handler (unfinished) 2024-06-26 13:52:00 +01:00
Dan Brown
59936631ec Lexical: Extracted mouse drag tracking to new helper 2024-06-25 18:33:29 +01:00
Dan Brown
3af22ce754 Lexical: Created custom table node with col width handling 2024-06-24 20:50:17 +01:00
Dan Brown
5546b8ff43 Lexical: Added more icons, made reflective text/bg color buttons 2024-06-23 15:50:41 +01:00
Dan Brown
a07092b7e6 Lexical: Updated lexical, added undo state tracking, format styles 2024-06-23 11:36:48 +01:00
Dan Brown
ac01c62e6e Lexical: Added table creator UI 2024-06-21 16:18:44 +01:00
Dan Brown
f47f7dd9d2 Lexical: Added base table support and started resize handling 2024-06-21 13:47:47 +01:00
Dan Brown
13d970c7ce Lexical: Added button icon system
With a bunch of default icons
2024-06-19 20:00:29 +01:00
Dan Brown
e2409a5fab Lexical: Added basic list button/support 2024-06-19 16:14:20 +01:00
Dan Brown
e30aae3399 Sponsors: Added Schroeck IT Consulting 2024-06-13 16:46:39 +01:00
Stefan Mueller
b81f2b52d0 Add ALLOWED_IFRAME_SOURCES to phpunit.xml
Fix for bug #5068
test_frame_src_csp_header_set fails, when .env-file has
customized ALLOWED_IFRAME_SOURCES
2024-06-13 12:41:05 +02:00
Dan Brown
9e43e03db4 Lexical: Added color picker controls 2024-06-12 19:51:42 +01:00
Dan Brown
a475cf68bf Lexical: Added clear formatting button 2024-06-12 14:24:50 +01:00
Dan Brown
e889bc680b Lexical: Added view/edit source code button/form/action 2024-06-12 14:01:36 +01:00
Dan Brown
48f235ea5a Updated version and assets for release v24.05.2 2024-06-10 11:44:06 +01:00
Dan Brown
047771b9f4 Merge branch 'development' into release 2024-06-10 11:43:05 +01:00
Dan Brown
c096b20d9c Updated translator & dependency attribution before release v24.05.2 2024-06-10 11:42:37 +01:00
Dan Brown
11a7ccc37e SAML: Set static type to pass static checks
Not totally clear if underlying code can actually return null, but
playing it safe to remain as-is for now for patch release.
2024-06-10 10:31:35 +01:00
Dan Brown
d9b9e6c0b1 Updated translations with latest Crowdin changes (#5022) 2024-06-10 10:16:34 +01:00
Dan Brown
f18d42f08e Merge pull request #5036 from bradenterpstra01/development
Fixed incorrect code shortcut reference
2024-06-09 23:23:28 +01:00
Dan Brown
4986f008b9 Merge pull request #5052 from michaelortnerit/development
Update docker-compose.yml
2024-06-09 23:20:01 +01:00
Dan Brown
a8ce199e0d Pages: Fixed unused changelog on first page publish
Included test to cover.
For #5056
2024-06-09 17:18:23 +01:00
Dan Brown
c77e8730d6 Deps: Updated php packages via composer 2024-06-09 17:03:29 +01:00
Dan Brown
3406846c82 Images: Updated GIF handling to use native methods
Changes GIF image thumbnail handling to direcly load via gd instead of
going through interventions own handling (which supports frames) since
we don't need animation for our thumbnails, and since performance issues
could arise with GIFs that have large frame counts.

For #5029
2024-06-09 17:00:58 +01:00
Dan Brown
bddc6ae66b Roles: Added max validation for role external auth id field
For #5037
2024-06-08 20:33:34 +01:00
Dan Brown
5c343638b6 Added base node/button for details/summary 2024-06-06 14:43:50 +01:00
Dan Brown
0722960260 Lexical: Added selection to state for aligned reading
Connected up to work with image form
2024-06-05 18:43:42 +01:00
Dan Brown
e959c468f6 Lexical: Made image resize handles functional 2024-06-05 17:18:58 +01:00
Dan Brown
ba871ec46a Lexical: Started image resize controls, Defined thorough decorator model 2024-06-05 13:04:49 +01:00
Michael Ortner
bd6e3c022f Update docker-compose.yml
Remove the version: because it is obsolete. See: https://docs.docker.com/compose/compose-file/04-version-and-name/#version-top-level-element-optional
2024-06-04 15:07:09 +02:00
Dan Brown
a74e04141c Lexical: Started build of image node and decoration UI 2024-06-03 16:56:31 +01:00
Dan Brown
7c504a10a8 Lexical: Created core modal functionality 2024-06-01 16:49:47 +01:00
Dan Brown
ae98745439 Lexical: Started on form UI 2024-05-30 16:50:55 +01:00
Dan Brown
57259aee00 Lexical: Added format previews to format buttons 2024-05-30 12:25:25 +01:00
bradenterpstra01
8759fff116 Update wysiwyg.blade.php
Remove the Shift for the numeric shortcut for incline code.

Ctrl+8 instead of Ctrl+Shift+8

I assume Mac is the same but I do not have a Mac to test with.
2024-05-29 18:01:48 -04:00
Dan Brown
dc1a40ea74 Lexical: Added ui container type
Structured UI logical to be fairly standard and mostly covered via
a base class that handles context and core dom work.
2024-05-29 20:38:31 +01:00
Dan Brown
483d9bf26c Lexical: Added a range of format buttons 2024-05-28 22:56:58 +01:00
Dan Brown
b24d60e98d Lexical: Started UI fundementals with basic button 2024-05-28 18:04:48 +01:00
Dan Brown
0f8bd869d8 Lexical: Added custom id-supporting paragraph blocks 2024-05-28 15:09:50 +01:00
Dan Brown
49546cd627 Lexical: Switched to ts for new editor build 2024-05-27 23:50:28 +01:00
Dan Brown
6e852d2e65 Lexical: Played with commands, extracted & improved callout node 2024-05-27 20:23:45 +01:00
Dan Brown
5a4f595341 Editors: Added lexical editor for testing
Started basic playground for testing lexical as a new WYSIWYG editor.
Moved out tinymce to be under wysiwyg-tinymce instead so lexical is the
default, but TinyMce code remains.
2024-05-27 15:39:41 +01:00
Dan Brown
6019d2ee14 MFA: Tweaked backup code wording
It was not clear before as it could be taken that the system would
securely store the codes.

Closes #5017
2024-05-23 11:30:53 +01:00
Dan Brown
b5375114d3 Updated version and assets for release v24.05.1 2024-05-21 11:07:36 +01:00
Dan Brown
fc13e56cea Merge branch 'development' into release 2024-05-21 11:07:10 +01:00
Dan Brown
f937bf3abb Updated translator & dependency attribution before release v24.05.1 2024-05-21 11:06:08 +01:00
Dan Brown
586e8963a8 Updated translations with latest Crowdin changes (#4994) 2024-05-21 11:04:27 +01:00
Dan Brown
bdfa76ed9a Deps: Updated php/composer packages 2024-05-20 17:28:53 +01:00
Dan Brown
d133f904d3 Auth: Changed email confirmations to use login attempt user
Negates the need for a public confirmation resend form
since we can instead just send direct to the last session login attempter.
2024-05-20 17:23:15 +01:00
Dan Brown
69af9e0dbd Routes: Added throttling to a range of auth-related endpoints
Some already throttled in some means, but this adds a simple ip-based
non-request-specific layer to many endpoints.
Related to #4993
2024-05-20 14:00:58 +01:00
Dan Brown
72c5141dec File Uploads: Added basic validation response formatting
Tested via app-level validation file limit, and then also with nginx
file post limit.
For #4996
2024-05-18 21:18:15 +01:00
Dan Brown
5651d2c43d Config: Reverted change to cache directory
Change made during Laravel 10 updates to align (Laravel made this change
much earlier in 5.x series) but it caused issues due to folder not
pre-existing and due to potentiall permission issues.
(CLI could create this during update, with non-compatible permissions
for webserver).

For #4999
2024-05-18 20:40:26 +01:00
Dan Brown
fc236f930b Dark Mode: Fixed setting labels missing dark mode handling
Fixes #5018
2024-05-18 20:37:49 +01:00
Dan Brown
570af500f4 WYSIWYG: Added justify cell range cleanup
To help override & gain control of setting text alignment in tables.

- Adds support of clearing "align" attributes in certain operations.
- Updates cell range action handling to dedupe execcommand handling.
- Adds clearing of additional alignment classes on direction control.

Closes #5011
2024-05-16 14:59:30 +01:00
Dan Brown
38913288d8 Devdocs: Fixed visual theme system lang folder reference
Made some other minor updates while there.
Fixes #4998
2024-05-16 14:15:26 +01:00
Dan Brown
c14d7d9509 Merge pull request #5008 from KiDxS/fix-notification-preferences-url-in-email
Fixed notification preferences URL in email
2024-05-16 14:11:15 +01:00
Angelo Geant Gaviola
79f5be4170 Fixed notification preferences URL in email 2024-05-14 17:04:23 +08:00
765 changed files with 74536 additions and 5811 deletions

View File

@@ -334,6 +334,11 @@ EXPORT_PAGE_SIZE=a4
# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
EXPORT_PDF_COMMAND=false
# Export PDF Command Timeout
# The number of seconds that the export PDF command will run before a timeout occurs.
# Only applies for the EXPORT_PDF_COMMAND option, not for DomPDF or wkhtmltopdf.
EXPORT_PDF_COMMAND_TIMEOUT=15
# Set path to wkhtmltopdf binary for PDF generation.
# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
# When false, BookStack will attempt to find a wkhtmltopdf in the application

View File

@@ -141,7 +141,7 @@ Kauê Sena (kaue.sena.ks) :: Portuguese, Brazilian
MatthieuParis :: French
Douradinho :: Portuguese, Brazilian; Portuguese
Gaku Yaguchi (tama11) :: Japanese
johnroyer :: Chinese Traditional
Zero Huang (johnroyer) :: Chinese Traditional
jackaaa :: Chinese Traditional
Irfan Hukama Arsyad (IrfanArsyad) :: Indonesian
Jeff Huang (s8321414) :: Chinese Traditional
@@ -347,7 +347,7 @@ Taygun Yıldırım (yildirimtaygun) :: Turkish
robing29 :: German
Bruno Eduardo de Jesus Barroso (brunoejb) :: Portuguese, Brazilian
Igor V Belousov (biv) :: Russian
David Bauer (davbauer) :: German
David Bauer (davbauer) :: German; German Informal
Guttorm Hveem (guttormhveem) :: Norwegian Nynorsk; Norwegian Bokmal
Minh Giang Truong (minhgiang1204) :: Vietnamese
Ioannis Ioannides (i.ioannides) :: Greek
@@ -389,7 +389,7 @@ Marc Hagen (MarcHagen) :: Dutch
Kasper Alsøe (zeonos) :: Danish
sultani :: Persian
renge :: Korean
Tim (thegatesdev) :: Dutch; German Informal; Romanian; French; Catalan; Czech; Danish; German; Finnish; Hungarian; Italian; Japanese; Korean; Polish; Russian; Ukrainian; Chinese Simplified; Chinese Traditional; Portuguese, Brazilian; Persian; Spanish, Argentina; Croatian; Norwegian Nynorsk; Estonian; Uzbek; Norwegian Bokmal
Tim (thegatesdev) :: Dutch; German Informal; French; Romanian; Catalan; Czech; Danish; German; Finnish; Hungarian; Italian; Japanese; Korean; Polish; Russian; Ukrainian; Chinese Simplified; Chinese Traditional; Portuguese, Brazilian; Persian; Spanish, Argentina; Croatian; Norwegian Nynorsk; Estonian; Uzbek; Norwegian Bokmal
Irdi (irdiOL) :: Albanian
KateBarber :: Welsh
Twister (theuncles75) :: Hebrew
@@ -422,3 +422,36 @@ crow_ :: Latvian
JocelynDelalande :: French
Jan (JW-CH) :: German Informal
Timo B (lommes) :: German Informal
Erik Lundstedt (Erik.Lundstedt) :: Swedish
yngams (younessmouhid) :: Arabic
Ohadp :: Hebrew
cbridi :: Portuguese, Brazilian
nanangsb :: Indonesian
Michal Melich (michalmelich) :: Czech
David (david-prv) :: German; German Informal
Larry (lahoje) :: Swedish
Marcia dos Santos (marciab80) :: Portuguese
Ricard López Torres (richilpez.torres) :: Catalan
sarahalves7 :: Portuguese, Brazilian
petr.husak :: Czech
javadataherian :: Persian
Ludo-code :: French
hollsten :: Swedish
Ngoc Lan Phung (lanpncz) :: Vietnamese
Worive :: Catalan
Илья Скаба (skabailya) :: Russian
Irjan Olsen (Irch) :: Norwegian Bokmal
Aleksandar Jovanovic (jovanoviczaleksandar) :: Serbian (Cyrillic)
Red (RedVortex) :: Hebrew
xgrug :: Chinese Simplified
HrCalmar :: Danish
Avishay Rapp (AvishayRapp) :: Hebrew
matthias4217 :: French
Berke BOYLU2 (berkeboylu2) :: Turkish
etwas7B :: German
Mohammed srhiri (m.sghiri20) :: Arabic
YongMin Kim (kym0118) :: Korean
Rivo Zängov (Eraser) :: Estonian
Francisco Rafael Fonseca (chicoraf) :: Portuguese, Brazilian
ИEØ_ΙΙØZ (NEO_IIOZ) :: Chinese Traditional
madnjpn (madnjpn.) :: Georgian

View File

@@ -13,9 +13,9 @@ on:
jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Install NPM deps
run: npm ci

29
.github/workflows/test-js.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: test-js
on:
push:
paths:
- '**.js'
- '**.ts'
- '**.json'
pull_request:
paths:
- '**.js'
- '**.ts'
- '**.json'
jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install NPM deps
run: npm ci
- name: Run TypeScript type checking
run: npm run ts:lint
- name: Run JavaScript tests
run: npm run test

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
/node_modules
/.vscode
/composer
/coverage
Homestead.yaml
.env
.idea

View File

@@ -32,13 +32,17 @@ class ConfirmEmailController extends Controller
/**
* Shows a notice that a user's email address has not been confirmed,
* Also has the option to re-send the confirmation email.
* along with the option to re-send the confirmation email.
*/
public function showAwaiting()
{
$user = $this->loginService->getLastLoginAttemptUser();
if ($user === null) {
$this->showErrorNotification(trans('errors.login_user_not_found'));
return redirect('/login');
}
return view('auth.user-unconfirmed', ['user' => $user]);
return view('auth.register-confirm-awaiting');
}
/**
@@ -90,19 +94,24 @@ class ConfirmEmailController extends Controller
/**
* Resend the confirmation email.
*/
public function resend(Request $request)
public function resend()
{
$this->validate($request, [
'email' => ['required', 'email', 'exists:users,email'],
]);
$user = $this->userRepo->getByEmail($request->get('email'));
$user = $this->loginService->getLastLoginAttemptUser();
if ($user === null) {
$this->showErrorNotification(trans('errors.login_user_not_found'));
return redirect('/login');
}
try {
$this->emailConfirmationService->sendConfirmation($user);
} catch (ConfirmationEmailException $e) {
$this->showErrorNotification($e->getMessage());
return redirect('/login');
} catch (Exception $e) {
$this->showErrorNotification(trans('auth.email_confirm_send_error'));
return redirect('/register/confirm');
return redirect('/register/awaiting');
}
$this->showSuccessNotification(trans('auth.email_confirm_resent'));

View File

@@ -6,6 +6,7 @@ use BookStack\Activity\ActivityType;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Sleep;
class ForgotPasswordController extends Controller
{
@@ -32,6 +33,10 @@ class ForgotPasswordController extends Controller
'email' => ['required', 'email'],
]);
// Add random pause to the response to help avoid time-base sniffing
// of valid resets via slower email send handling.
Sleep::for(random_int(1000, 3000))->milliseconds();
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.

View File

@@ -17,7 +17,7 @@ trait HandlesPartialLogins
$user = auth()->user() ?? $loginService->getLastLoginAttemptUser();
if (!$user) {
throw new NotFoundException('A user for this action could not be found');
throw new NotFoundException(trans('errors.login_user_not_found'));
}
return $user;

View File

@@ -15,14 +15,11 @@ use Illuminate\Validation\Rules\Password as PasswordRule;
class ResetPasswordController extends Controller
{
protected LoginService $loginService;
public function __construct(LoginService $loginService)
{
public function __construct(
protected LoginService $loginService
) {
$this->middleware('guest');
$this->middleware('guard:standard');
$this->loginService = $loginService;
}
/**

View File

@@ -17,7 +17,7 @@ class EmailConfirmationService extends UserTokenService
*
* @throws ConfirmationEmailException
*/
public function sendConfirmation(User $user)
public function sendConfirmation(User $user): void
{
if ($user->email_confirmed) {
throw new ConfirmationEmailException(trans('errors.email_already_confirmed'), '/login');

View File

@@ -52,13 +52,25 @@ class Ldap
*
* @param resource|\LDAP\Connection $ldapConnection
*
* @return resource|\LDAP\Result
* @return \LDAP\Result|array|false
*/
public function search($ldapConnection, string $baseDn, string $filter, array $attributes = null)
{
return ldap_search($ldapConnection, $baseDn, $filter, $attributes);
}
/**
* Read an entry from the LDAP tree.
*
* @param resource|\Ldap\Connection $ldapConnection
*
* @return \LDAP\Result|array|false
*/
public function read($ldapConnection, string $baseDn, string $filter, array $attributes = null)
{
return ldap_read($ldapConnection, $baseDn, $filter, $attributes);
}
/**
* Get entries from an LDAP search result.
*

View File

@@ -321,94 +321,105 @@ class LdapService
return [];
}
$userGroups = $this->groupFilter($user);
$userGroups = $this->extractGroupsFromSearchResponseEntry($user);
$allGroups = $this->getGroupsRecursive($userGroups, []);
$formattedGroups = $this->extractGroupNamesFromLdapGroupDns($allGroups);
if ($this->config['dump_user_groups']) {
throw new JsonDebugException([
'details_from_ldap' => $user,
'parsed_direct_user_groups' => $userGroups,
'parsed_recursive_user_groups' => $allGroups,
'details_from_ldap' => $user,
'parsed_direct_user_groups' => $userGroups,
'parsed_recursive_user_groups' => $allGroups,
'parsed_resulting_group_names' => $formattedGroups,
]);
}
return $allGroups;
return $formattedGroups;
}
protected function extractGroupNamesFromLdapGroupDns(array $groupDNs): array
{
$names = [];
foreach ($groupDNs as $groupDN) {
$exploded = $this->ldap->explodeDn($groupDN, 1);
if ($exploded !== false && count($exploded) > 0) {
$names[] = $exploded[0];
}
}
return array_unique($names);
}
/**
* Get the parent groups of an array of groups.
* Build an array of all relevant groups DNs after recursively scanning
* across parents of the groups given.
*
* @throws LdapException
*/
private function getGroupsRecursive(array $groupsArray, array $checked): array
protected function getGroupsRecursive(array $groupDNs, array $checked): array
{
$groupsToAdd = [];
foreach ($groupsArray as $groupName) {
if (in_array($groupName, $checked)) {
foreach ($groupDNs as $groupDN) {
if (in_array($groupDN, $checked)) {
continue;
}
$parentGroups = $this->getGroupGroups($groupName);
$parentGroups = $this->getParentsOfGroup($groupDN);
$groupsToAdd = array_merge($groupsToAdd, $parentGroups);
$checked[] = $groupName;
$checked[] = $groupDN;
}
$groupsArray = array_unique(array_merge($groupsArray, $groupsToAdd), SORT_REGULAR);
$uniqueDNs = array_unique(array_merge($groupDNs, $groupsToAdd), SORT_REGULAR);
if (empty($groupsToAdd)) {
return $groupsArray;
return $uniqueDNs;
}
return $this->getGroupsRecursive($groupsArray, $checked);
return $this->getGroupsRecursive($uniqueDNs, $checked);
}
/**
* Get the parent groups of a single group.
*
* @throws LdapException
*/
private function getGroupGroups(string $groupName): array
protected function getParentsOfGroup(string $groupDN): array
{
$groupsAttr = strtolower($this->config['group_attribute']);
$ldapConnection = $this->getConnection();
$this->bindSystemUser($ldapConnection);
$followReferrals = $this->config['follow_referrals'] ? 1 : 0;
$this->ldap->setOption($ldapConnection, LDAP_OPT_REFERRALS, $followReferrals);
$baseDn = $this->config['base_dn'];
$groupsAttr = strtolower($this->config['group_attribute']);
$groupFilter = 'CN=' . $this->ldap->escape($groupName);
$groups = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $groupFilter, [$groupsAttr]);
if ($groups['count'] === 0) {
$read = $this->ldap->read($ldapConnection, $groupDN, '(objectClass=*)', [$groupsAttr]);
$results = $this->ldap->getEntries($ldapConnection, $read);
if ($results['count'] === 0) {
return [];
}
return $this->groupFilter($groups[0]);
return $this->extractGroupsFromSearchResponseEntry($results[0]);
}
/**
* Filter out LDAP CN and DN language in a ldap search return.
* Gets the base CN (common name) of the string.
* Extract an array of group DN values from the given LDAP search response entry
*/
protected function groupFilter(array $userGroupSearchResponse): array
protected function extractGroupsFromSearchResponseEntry(array $ldapEntry): array
{
$groupsAttr = strtolower($this->config['group_attribute']);
$ldapGroups = [];
$groupDNs = [];
$count = 0;
if (isset($userGroupSearchResponse[$groupsAttr]['count'])) {
$count = (int) $userGroupSearchResponse[$groupsAttr]['count'];
if (isset($ldapEntry[$groupsAttr]['count'])) {
$count = (int) $ldapEntry[$groupsAttr]['count'];
}
for ($i = 0; $i < $count; $i++) {
$dnComponents = $this->ldap->explodeDn($userGroupSearchResponse[$groupsAttr][$i], 1);
if (!in_array($dnComponents[0], $ldapGroups)) {
$ldapGroups[] = $dnComponents[0];
$dn = $ldapEntry[$groupsAttr][$i];
if (!in_array($dn, $groupDNs)) {
$groupDNs[] = $dn;
}
}
return $ldapGroups;
return $groupDNs;
}
/**

View File

@@ -22,7 +22,7 @@ class OidcUserDetails
$hasEmpty = empty($this->externalId)
|| empty($this->email)
|| empty($this->name)
|| ($groupSyncActive && empty($this->groups));
|| ($groupSyncActive && $this->groups === null);
return !$hasEmpty;
}
@@ -57,15 +57,15 @@ class OidcUserDetails
return implode(' ', $displayName);
}
protected static function getUserGroups(string $groupsClaim, ProvidesClaims $token): array
protected static function getUserGroups(string $groupsClaim, ProvidesClaims $token): ?array
{
if (empty($groupsClaim)) {
return [];
return null;
}
$groupsList = Arr::get($token->getAllClaims(), $groupsClaim);
if (!is_array($groupsList)) {
return [];
return null;
}
return array_values(array_filter($groupsList, function ($val) {

View File

@@ -11,7 +11,9 @@ class OidcUserinfoResponse implements ProvidesClaims
public function __construct(ResponseInterface $response, string $issuer, array $keys)
{
$contentType = $response->getHeader('Content-Type')[0];
$contentTypeHeaderValue = $response->getHeader('Content-Type')[0] ?? '';
$contentType = strtolower(trim(explode(';', $contentTypeHeaderValue, 2)[0]));
if ($contentType === 'application/json') {
$this->claims = json_decode($response->getBody()->getContents(), true);
}

View File

@@ -133,6 +133,7 @@ class Saml2Service
// value so that the exact encoding format is matched when checking the signature.
// This is primarily due to ADFS encoding query params with lowercase percent encoding while
// PHP (And most other sensible providers) standardise on uppercase.
/** @var ?string $samlRedirect */
$samlRedirect = $toolkit->processSLO(true, $requestId, true, null, true);
$errors = $toolkit->getErrors();

View File

@@ -0,0 +1,10 @@
<?php
namespace BookStack\Access;
use Exception;
class UserInviteException extends Exception
{
//
}

View File

@@ -13,11 +13,17 @@ class UserInviteService extends UserTokenService
/**
* Send an invitation to a user to sign into BookStack
* Removes existing invitation tokens.
* @throws UserInviteException
*/
public function sendInvitation(User $user)
{
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
$user->notify(new UserInviteNotification($token));
try {
$user->notify(new UserInviteNotification($token));
} catch (\Exception $exception) {
throw new UserInviteException($exception->getMessage(), $exception->getCode(), $exception);
}
}
}

View File

@@ -43,7 +43,7 @@ abstract class BaseActivityNotification extends MailNotification
protected function buildReasonFooterLine(LocaleDefinition $locale): LinkedMailMessageLine
{
return new LinkedMailMessageLine(
url('/preferences/notifications'),
url('/my-account/notifications'),
$locale->trans('notifications.footer_reason'),
$locale->trans('notifications.footer_reason_link'),
);

View File

@@ -64,4 +64,14 @@ class MetaController extends Controller
'jsLibData' => file_get_contents(base_path('dev/licensing/js-library-licenses.txt')),
]);
}
/**
* Show the view for /opensearch.xml.
*/
public function opensearch()
{
return response()
->view('misc.opensearch')
->header('Content-Type', 'application/opensearchdescription+xml');
}
}

View File

@@ -81,5 +81,9 @@ class RouteServiceProvider extends ServiceProvider
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('public', function (Request $request) {
return Limit::perMinute(10)->by($request->ip());
});
}
}

View File

@@ -53,8 +53,8 @@ return [
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
'lock_path' => storage_path('framework/cache/data'),
'path' => storage_path('framework/cache'),
'lock_path' => storage_path('framework/cache'),
],
'memcached' => [

View File

@@ -29,6 +29,10 @@ return [
// Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
'pdf_command' => env('EXPORT_PDF_COMMAND', false),
// The amount of time allowed for PDF generation command to run
// before the process times out and is stopped.
'pdf_command_timeout' => env('EXPORT_PDF_COMMAND_TIMEOUT', 15),
// 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support.
'snappy' => [
'pdf_binary' => env('WKHTMLTOPDF', false),

View File

@@ -49,6 +49,7 @@ class UpdateUrlCommand extends Command
'chapters' => ['description_html'],
'books' => ['description_html'],
'bookshelves' => ['description_html'],
'page_revisions' => ['html', 'text', 'markdown'],
'images' => ['url'],
'settings' => ['value'],
'comments' => ['html', 'text'],
@@ -77,6 +78,12 @@ class UpdateUrlCommand extends Command
$this->info('URL update procedure complete.');
$this->info('============================================================================');
$this->info('Be sure to run "php artisan cache:clear" to clear any old URLs in the cache.');
if (!str_starts_with($newUrl, url('/'))) {
$this->warn('You still need to update your APP_URL env value. This is currently set to:');
$this->warn(url('/'));
}
$this->info('============================================================================');
return 0;

View File

@@ -7,6 +7,7 @@ use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Queries\BookQueries;
use BookStack\Entities\Queries\PageQueries;
use BookStack\Entities\Repos\BookRepo;
use BookStack\Entities\Tools\BookContents;
use BookStack\Http\ApiController;
@@ -18,6 +19,7 @@ class BookApiController extends ApiController
public function __construct(
protected BookRepo $bookRepo,
protected BookQueries $queries,
protected PageQueries $pageQueries,
) {
}
@@ -69,7 +71,8 @@ class BookApiController extends ApiController
->withType()
->withField('pages', function (Entity $entity) {
if ($entity instanceof Chapter) {
return (new ApiEntityListFormatter($entity->pages->all()))->format();
$pages = $this->pageQueries->visibleForChapterList($entity->id)->get()->all();
return (new ApiEntityListFormatter($pages))->format();
}
return null;
})->format();

View File

@@ -3,6 +3,7 @@
namespace BookStack\Entities\Models;
use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Uploads\Attachment;
use Illuminate\Database\Eloquent\Builder;

View File

@@ -11,7 +11,7 @@ use BookStack\Entities\Models\PageRevision;
use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorData;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\PermissionsException;
@@ -43,6 +43,7 @@ class PageRepo
'owned_by' => user()->id,
'updated_by' => user()->id,
'draft' => true,
'editor' => PageEditorType::getSystemDefault()->value,
]);
if ($parent instanceof Chapter) {
@@ -77,7 +78,8 @@ class PageRepo
$this->updateTemplateStatusAndContentFromInput($draft, $input);
$this->baseRepo->update($draft, $input);
$this->revisionRepo->storeNewForPage($draft, trans('entities.pages_initial_revision'));
$summary = trim($input['summary'] ?? '') ?: trans('entities.pages_initial_revision');
$this->revisionRepo->storeNewForPage($draft, $summary);
$draft->refresh();
Activity::add(ActivityType::PAGE_CREATE, $draft);
@@ -126,7 +128,9 @@ class PageRepo
}
$pageContent = new PageContent($page);
$currentEditor = $page->editor ?: PageEditorData::getSystemDefaultEditor();
$defaultEditor = PageEditorType::getSystemDefault();
$currentEditor = PageEditorType::forPage($page) ?: $defaultEditor;
$inputEditor = PageEditorType::fromRequestValue($input['editor'] ?? '') ?? $currentEditor;
$newEditor = $currentEditor;
$haveInput = isset($input['markdown']) || isset($input['html']);
@@ -135,15 +139,17 @@ class PageRepo
if ($haveInput && $inputEmpty) {
$pageContent->setNewHTML('', user());
} elseif (!empty($input['markdown']) && is_string($input['markdown'])) {
$newEditor = 'markdown';
$newEditor = PageEditorType::Markdown;
$pageContent->setNewMarkdown($input['markdown'], user());
} elseif (isset($input['html'])) {
$newEditor = 'wysiwyg';
$newEditor = ($inputEditor->isHtmlBased() ? $inputEditor : null) ?? ($defaultEditor->isHtmlBased() ? $defaultEditor : null) ?? PageEditorType::WysiwygTinymce;
$pageContent->setNewHTML($input['html'], user());
}
if ($newEditor !== $currentEditor && userCan('editor-change')) {
$page->editor = $newEditor;
if (($newEditor !== $currentEditor || empty($page->editor)) && userCan('editor-change')) {
$page->editor = $newEditor->value;
} elseif (empty($page->editor)) {
$page->editor = $defaultEditor->value;
}
}

View File

@@ -74,17 +74,17 @@ class PageEditorData
];
}
protected function updateContentForEditor(Page $page, string $editorType): void
protected function updateContentForEditor(Page $page, PageEditorType $editorType): void
{
$isHtml = !empty($page->html) && empty($page->markdown);
// HTML to markdown-clean conversion
if ($editorType === 'markdown' && $isHtml && $this->requestedEditor === 'markdown-clean') {
if ($editorType === PageEditorType::Markdown && $isHtml && $this->requestedEditor === 'markdown-clean') {
$page->markdown = (new HtmlToMarkdown($page->html))->convert();
}
// Markdown to HTML conversion if we don't have HTML
if ($editorType === 'wysiwyg' && !$isHtml) {
if ($editorType->isHtmlBased() && !$isHtml) {
$page->html = (new MarkdownToHtml($page->markdown))->convert();
}
}
@@ -94,24 +94,16 @@ class PageEditorData
* Defaults based upon the current content of the page otherwise will fall back
* to system default but will take a requested type (if provided) if permissions allow.
*/
protected function getEditorType(Page $page): string
protected function getEditorType(Page $page): PageEditorType
{
$editorType = $page->editor ?: self::getSystemDefaultEditor();
$editorType = PageEditorType::forPage($page) ?: PageEditorType::getSystemDefault();
// Use requested editor if valid and if we have permission
$requestedType = explode('-', $this->requestedEditor)[0];
if (($requestedType === 'markdown' || $requestedType === 'wysiwyg') && userCan('editor-change')) {
$requestedType = PageEditorType::fromRequestValue($this->requestedEditor);
if ($requestedType && userCan('editor-change')) {
$editorType = $requestedType;
}
return $editorType;
}
/**
* Get the configured system default editor.
*/
public static function getSystemDefaultEditor(): string
{
return setting('app-editor') === 'markdown' ? 'markdown' : 'wysiwyg';
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace BookStack\Entities\Tools;
use BookStack\Entities\Models\Page;
enum PageEditorType: string
{
case WysiwygTinymce = 'wysiwyg';
case WysiwygLexical = 'wysiwyg2024';
case Markdown = 'markdown';
public function isHtmlBased(): bool
{
return match ($this) {
self::WysiwygTinymce, self::WysiwygLexical => true,
self::Markdown => false,
};
}
public static function fromRequestValue(string $value): static|null
{
$editor = explode('-', $value)[0];
return static::tryFrom($editor);
}
public static function forPage(Page $page): static|null
{
return static::tryFrom($page->editor);
}
public static function getSystemDefault(): static
{
$setting = setting('app-editor');
return static::tryFrom($setting) ?? static::WysiwygTinymce;
}
}

View File

@@ -104,10 +104,10 @@ class PageIncludeParser
if ($currentOffset < $tagStartOffset) {
$previousText = substr($text, $currentOffset, $tagStartOffset - $currentOffset);
$textNode->parentNode->insertBefore(new DOMText($previousText), $textNode);
$textNode->parentNode->insertBefore($this->doc->createTextNode($previousText), $textNode);
}
$node = $textNode->parentNode->insertBefore(new DOMText($tagOuterContent), $textNode);
$node = $textNode->parentNode->insertBefore($this->doc->createTextNode($tagOuterContent), $textNode);
$includeTags[] = new PageIncludeTag($tagInnerContent, $node);
$currentOffset = $tagStartOffset + strlen($tagOuterContent);
}

View File

@@ -5,6 +5,7 @@ namespace BookStack\Entities\Tools;
use BookStack\Exceptions\PdfExportException;
use Knp\Snappy\Pdf as SnappyPdf;
use Dompdf\Dompdf;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process;
class PdfGenerator
@@ -85,9 +86,15 @@ class PdfGenerator
file_put_contents($inputHtml, $html);
$timeout = intval(config('exports.pdf_command_timeout'));
$process = Process::fromShellCommandline($command);
$process->setTimeout(15);
$process->run();
$process->setTimeout($timeout);
try {
$process->run();
} catch (ProcessTimedOutException $e) {
throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)");
}
if (!$process->isSuccessful()) {
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");

View File

@@ -9,16 +9,10 @@ use Illuminate\Http\Request;
class StoppedAuthenticationException extends \Exception implements Responsable
{
protected $user;
protected $loginService;
/**
* StoppedAuthenticationException constructor.
*/
public function __construct(User $user, LoginService $loginService)
{
$this->user = $user;
$this->loginService = $loginService;
public function __construct(
protected User $user,
protected LoginService $loginService
) {
parent::__construct();
}

View File

@@ -92,7 +92,7 @@ class RangeSupportedStream
if ($start < 0 || $start > $end) {
$this->responseStatus = 416;
$this->responseHeaders['Content-Range'] = sprintf('bytes */%s', $this->fileSize);
} elseif ($end - $start < $this->fileSize - 1) {
} else {
$this->responseLength = $end < $this->fileSize ? $end - $start + 1 : -1;
$this->responseOffset = $start;
$this->responseStatus = 206;

View File

@@ -0,0 +1,13 @@
<?php
namespace BookStack\Search\Options;
class ExactSearchOption extends SearchOption
{
public function toString(): string
{
$escaped = str_replace('\\', '\\\\', $this->value);
$escaped = str_replace('"', '\"', $escaped);
return ($this->negated ? '-' : '') . '"' . $escaped . '"';
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace BookStack\Search\Options;
class FilterSearchOption extends SearchOption
{
protected string $name;
public function __construct(
string $value,
string $name,
bool $negated = false,
) {
parent::__construct($value, $negated);
$this->name = $name;
}
public function toString(): string
{
$valueText = ($this->value ? ':' . $this->value : '');
$filterBrace = '{' . $this->name . $valueText . '}';
return ($this->negated ? '-' : '') . $filterBrace;
}
public function getKey(): string
{
return $this->name;
}
public static function fromContentString(string $value, bool $negated = false): self
{
$explodedFilter = explode(':', $value, 2);
$filterValue = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
$filterName = $explodedFilter[0];
return new self($filterValue, $filterName, $negated);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace BookStack\Search\Options;
abstract class SearchOption
{
public function __construct(
public string $value,
public bool $negated = false,
) {
}
/**
* Get the key used for this option when used in a map.
* Null indicates to use the index of the containing array.
*/
public function getKey(): string|null
{
return null;
}
/**
* Get the search string representation for this search option.
*/
abstract public function toString(): string;
}

View File

@@ -0,0 +1,37 @@
<?php
namespace BookStack\Search\Options;
class TagSearchOption extends SearchOption
{
/**
* Acceptable operators to be used within a tag search option.
*
* @var string[]
*/
protected array $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
public function toString(): string
{
return ($this->negated ? '-' : '') . "[{$this->value}]";
}
/**
* @return array{name: string, operator: string, value: string}
*/
public function getParts(): array
{
$operatorRegex = implode('|', array_map(fn($op) => preg_quote($op), $this->queryOperators));
preg_match('/^(.*?)((' . $operatorRegex . ')(.*?))?$/', $this->value, $tagSplit);
$extractedOperator = count($tagSplit) > 2 ? $tagSplit[3] : '';
$tagOperator = in_array($extractedOperator, $this->queryOperators) ? $extractedOperator : '=';
$tagValue = count($tagSplit) > 3 ? $tagSplit[4] : '';
return [
'name' => $tagSplit[1],
'operator' => $tagOperator,
'value' => $tagValue,
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace BookStack\Search\Options;
class TermSearchOption extends SearchOption
{
public function toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace BookStack\Search;
use BookStack\Search\Options\SearchOption;
/**
* @template T of SearchOption
*/
class SearchOptionSet
{
/**
* @var T[]
*/
protected array $options = [];
public function __construct(array $options = [])
{
$this->options = $options;
}
public function toValueArray(): array
{
return array_map(fn(SearchOption $option) => $option->value, $this->options);
}
public function toValueMap(): array
{
$map = [];
foreach ($this->options as $index => $option) {
$key = $option->getKey() ?? $index;
$map[$key] = $option->value;
}
return $map;
}
public function merge(SearchOptionSet $set): self
{
return new self(array_merge($this->options, $set->options));
}
public function filterEmpty(): self
{
$filteredOptions = array_values(array_filter($this->options, fn (SearchOption $option) => !empty($option->value)));
return new self($filteredOptions);
}
/**
* @param class-string<SearchOption> $class
*/
public static function fromValueArray(array $values, string $class): self
{
$options = array_map(fn($val) => new $class($val), $values);
return new self($options);
}
/**
* @return T[]
*/
public function all(): array
{
return $this->options;
}
/**
* @return self<T>
*/
public function negated(): self
{
$values = array_values(array_filter($this->options, fn (SearchOption $option) => $option->negated));
return new self($values);
}
/**
* @return self<T>
*/
public function nonNegated(): self
{
$values = array_values(array_filter($this->options, fn (SearchOption $option) => !$option->negated));
return new self($values);
}
}

View File

@@ -2,26 +2,39 @@
namespace BookStack\Search;
use BookStack\Search\Options\ExactSearchOption;
use BookStack\Search\Options\FilterSearchOption;
use BookStack\Search\Options\SearchOption;
use BookStack\Search\Options\TagSearchOption;
use BookStack\Search\Options\TermSearchOption;
use Illuminate\Http\Request;
class SearchOptions
{
public array $searches = [];
public array $exacts = [];
public array $tags = [];
public array $filters = [];
/** @var SearchOptionSet<TermSearchOption> */
public SearchOptionSet $searches;
/** @var SearchOptionSet<ExactSearchOption> */
public SearchOptionSet $exacts;
/** @var SearchOptionSet<TagSearchOption> */
public SearchOptionSet $tags;
/** @var SearchOptionSet<FilterSearchOption> */
public SearchOptionSet $filters;
public function __construct()
{
$this->searches = new SearchOptionSet();
$this->exacts = new SearchOptionSet();
$this->tags = new SearchOptionSet();
$this->filters = new SearchOptionSet();
}
/**
* Create a new instance from a search string.
*/
public static function fromString(string $search): self
{
$decoded = static::decode($search);
$instance = new SearchOptions();
foreach ($decoded as $type => $value) {
$instance->$type = $value;
}
$instance = new self();
$instance->addOptionsFromString($search);
return $instance;
}
@@ -41,46 +54,64 @@ class SearchOptions
}
$instance = new SearchOptions();
$inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
$inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags', 'extras']);
$parsedStandardTerms = static::parseStandardTermString($inputs['search'] ?? '');
$instance->searches = array_filter($parsedStandardTerms['terms']);
$instance->exacts = array_filter($parsedStandardTerms['exacts']);
array_push($instance->exacts, ...array_filter($inputs['exact'] ?? []));
$instance->tags = array_filter($inputs['tags'] ?? []);
$inputExacts = array_filter($inputs['exact'] ?? []);
$instance->searches = SearchOptionSet::fromValueArray(array_filter($parsedStandardTerms['terms']), TermSearchOption::class);
$instance->exacts = SearchOptionSet::fromValueArray(array_filter($parsedStandardTerms['exacts']), ExactSearchOption::class);
$instance->exacts = $instance->exacts->merge(SearchOptionSet::fromValueArray($inputExacts, ExactSearchOption::class));
$instance->tags = SearchOptionSet::fromValueArray(array_filter($inputs['tags'] ?? []), TagSearchOption::class);
$cleanedFilters = [];
foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
if (empty($filterVal)) {
continue;
}
$instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal;
$cleanedFilterVal = $filterVal === 'true' ? '' : $filterVal;
$cleanedFilters[] = new FilterSearchOption($cleanedFilterVal, $filterKey);
}
if (isset($inputs['types']) && count($inputs['types']) < 4) {
$instance->filters['type'] = implode('|', $inputs['types']);
$cleanedFilters[] = new FilterSearchOption(implode('|', $inputs['types']), 'type');
}
$instance->filters = new SearchOptionSet($cleanedFilters);
// Parse and merge in extras if provided
if (!empty($inputs['extras'])) {
$extras = static::fromString($inputs['extras']);
$instance->searches = $instance->searches->merge($extras->searches);
$instance->exacts = $instance->exacts->merge($extras->exacts);
$instance->tags = $instance->tags->merge($extras->tags);
$instance->filters = $instance->filters->merge($extras->filters);
}
return $instance;
}
/**
* Decode a search string into an array of terms.
* Decode a search string and add its contents to this instance.
*/
protected static function decode(string $searchString): array
protected function addOptionsFromString(string $searchString): void
{
/** @var array<string, SearchOption[]> $terms */
$terms = [
'searches' => [],
'exacts' => [],
'tags' => [],
'filters' => [],
];
$patterns = [
'exacts' => '/"((?:\\\\.|[^"\\\\])*)"/',
'tags' => '/\[(.*?)\]/',
'filters' => '/\{(.*?)\}/',
'exacts' => '/-?"((?:\\\\.|[^"\\\\])*)"/',
'tags' => '/-?\[(.*?)\]/',
'filters' => '/-?\{(.*?)\}/',
];
$constructors = [
'exacts' => fn(string $value, bool $negated) => new ExactSearchOption($value, $negated),
'tags' => fn(string $value, bool $negated) => new TagSearchOption($value, $negated),
'filters' => fn(string $value, bool $negated) => FilterSearchOption::fromContentString($value, $negated),
];
// Parse special terms
@@ -88,34 +119,32 @@ class SearchOptions
$matches = [];
preg_match_all($pattern, $searchString, $matches);
if (count($matches) > 0) {
$terms[$termType] = $matches[1];
foreach ($matches[1] as $index => $value) {
$negated = str_starts_with($matches[0][$index], '-');
$terms[$termType][] = $constructors[$termType]($value, $negated);
}
$searchString = preg_replace($pattern, '', $searchString);
}
}
// Unescape exacts and backslash escapes
foreach ($terms['exacts'] as $index => $exact) {
$terms['exacts'][$index] = static::decodeEscapes($exact);
foreach ($terms['exacts'] as $exact) {
$exact->value = static::decodeEscapes($exact->value);
}
// Parse standard terms
$parsedStandardTerms = static::parseStandardTermString($searchString);
array_push($terms['searches'], ...$parsedStandardTerms['terms']);
array_push($terms['exacts'], ...$parsedStandardTerms['exacts']);
$this->searches = $this->searches
->merge(SearchOptionSet::fromValueArray($parsedStandardTerms['terms'], TermSearchOption::class))
->filterEmpty();
$this->exacts = $this->exacts
->merge(new SearchOptionSet($terms['exacts']))
->merge(SearchOptionSet::fromValueArray($parsedStandardTerms['exacts'], ExactSearchOption::class))
->filterEmpty();
// Split filter values out
$splitFilters = [];
foreach ($terms['filters'] as $filter) {
$explodedFilter = explode(':', $filter, 2);
$splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
}
$terms['filters'] = $splitFilters;
// Filter down terms where required
$terms['exacts'] = array_filter($terms['exacts']);
$terms['searches'] = array_filter($terms['searches']);
return $terms;
// Add tags & filters
$this->tags = $this->tags->merge(new SearchOptionSet($terms['tags']));
$this->filters = $this->filters->merge(new SearchOptionSet($terms['filters']));
}
/**
@@ -175,7 +204,9 @@ class SearchOptions
*/
public function setFilter(string $filterName, string $filterValue = ''): void
{
$this->filters[$filterName] = $filterValue;
$this->filters = $this->filters->merge(
new SearchOptionSet([new FilterSearchOption($filterValue, $filterName)])
);
}
/**
@@ -183,22 +214,43 @@ class SearchOptions
*/
public function toString(): string
{
$parts = $this->searches;
$options = [
...$this->searches->all(),
...$this->exacts->all(),
...$this->tags->all(),
...$this->filters->all(),
];
foreach ($this->exacts as $term) {
$escaped = str_replace('\\', '\\\\', $term);
$escaped = str_replace('"', '\"', $escaped);
$parts[] = '"' . $escaped . '"';
}
foreach ($this->tags as $term) {
$parts[] = "[{$term}]";
}
foreach ($this->filters as $filterName => $filterVal) {
$parts[] = '{' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
}
$parts = array_map(fn(SearchOption $o) => $o->toString(), $options);
return implode(' ', $parts);
}
/**
* Get the search options that don't have UI controls provided for.
* Provided back as a key => value array with the keys being expected
* input names for a search form, and values being the option value.
*/
public function getAdditionalOptionsString(): string
{
$options = [];
// Handle filters without UI support
$userFilters = ['updated_by', 'created_by', 'owned_by'];
$unsupportedFilters = ['is_template', 'sort_by'];
foreach ($this->filters->all() as $filter) {
if (in_array($filter->getKey(), $userFilters, true) && $filter->value !== null && $filter->value !== 'me') {
$options[] = $filter;
} else if (in_array($filter->getKey(), $unsupportedFilters, true)) {
$options[] = $filter;
}
}
// Negated items
array_push($options, ...$this->exacts->negated()->all());
array_push($options, ...$this->tags->negated()->all());
array_push($options, ...$this->filters->negated()->all());
return implode(' ', array_map(fn(SearchOption $o) => $o->toString(), $options));
}
}

View File

@@ -25,11 +25,12 @@ class SearchResultsFormatter
* Update the given entity model to set attributes used for previews of the item
* primarily within search result lists.
*/
protected function setSearchPreview(Entity $entity, SearchOptions $options)
protected function setSearchPreview(Entity $entity, SearchOptions $options): void
{
$textProperty = $entity->textField;
$textContent = $entity->$textProperty;
$terms = array_merge($options->exacts, $options->searches);
$relevantSearchOptions = $options->exacts->merge($options->searches);
$terms = $relevantSearchOptions->toValueArray();
$originalContentByNewAttribute = [
'preview_name' => $entity->name,

View File

@@ -7,6 +7,7 @@ use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Queries\EntityQueries;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Search\Options\TagSearchOption;
use BookStack\Users\Models\User;
use Illuminate\Database\Connection;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
@@ -16,31 +17,21 @@ use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use SplObjectStorage;
use WeakMap;
class SearchRunner
{
/**
* Acceptable operators to be used in a query.
*
* @var string[]
*/
protected array $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!='];
/**
* Retain a cache of score adjusted terms for specific search options.
* From PHP>=8 this can be made into a WeakMap instead.
*
* @var SplObjectStorage
*/
protected $termAdjustmentCache;
protected WeakMap $termAdjustmentCache;
public function __construct(
protected EntityProvider $entityProvider,
protected PermissionApplicator $permissions,
protected EntityQueries $entityQueries,
) {
$this->termAdjustmentCache = new SplObjectStorage();
$this->termAdjustmentCache = new WeakMap();
}
/**
@@ -55,10 +46,11 @@ class SearchRunner
$entityTypes = array_keys($this->entityProvider->all());
$entityTypesToSearch = $entityTypes;
$filterMap = $searchOpts->filters->toValueMap();
if ($entityType !== 'all') {
$entityTypesToSearch = [$entityType];
} elseif (isset($searchOpts->filters['type'])) {
$entityTypesToSearch = explode('|', $searchOpts->filters['type']);
} elseif (isset($filterMap['type'])) {
$entityTypesToSearch = explode('|', $filterMap['type']);
}
$results = collect();
@@ -97,7 +89,8 @@ class SearchRunner
{
$opts = SearchOptions::fromString($searchString);
$entityTypes = ['page', 'chapter'];
$entityTypesToSearch = isset($opts->filters['type']) ? explode('|', $opts->filters['type']) : $entityTypes;
$filterMap = $opts->filters->toValueMap();
$entityTypesToSearch = isset($filterMap['type']) ? explode('|', $filterMap['type']) : $entityTypes;
$results = collect();
foreach ($entityTypesToSearch as $entityType) {
@@ -161,24 +154,26 @@ class SearchRunner
$this->applyTermSearch($entityQuery, $searchOpts, $entityType);
// Handle exact term matching
foreach ($searchOpts->exacts as $inputTerm) {
$entityQuery->where(function (EloquentBuilder $query) use ($inputTerm, $entityModelInstance) {
$inputTerm = str_replace('\\', '\\\\', $inputTerm);
foreach ($searchOpts->exacts->all() as $exact) {
$filter = function (EloquentBuilder $query) use ($exact, $entityModelInstance) {
$inputTerm = str_replace('\\', '\\\\', $exact->value);
$query->where('name', 'like', '%' . $inputTerm . '%')
->orWhere($entityModelInstance->textField, 'like', '%' . $inputTerm . '%');
});
};
$exact->negated ? $entityQuery->whereNot($filter) : $entityQuery->where($filter);
}
// Handle tag searches
foreach ($searchOpts->tags as $inputTerm) {
$this->applyTagSearch($entityQuery, $inputTerm);
foreach ($searchOpts->tags->all() as $tagOption) {
$this->applyTagSearch($entityQuery, $tagOption);
}
// Handle filters
foreach ($searchOpts->filters as $filterTerm => $filterValue) {
$functionName = Str::camel('filter_' . $filterTerm);
foreach ($searchOpts->filters->all() as $filterOption) {
$functionName = Str::camel('filter_' . $filterOption->getKey());
if (method_exists($this, $functionName)) {
$this->$functionName($entityQuery, $entityModelInstance, $filterValue);
$this->$functionName($entityQuery, $entityModelInstance, $filterOption->value, $filterOption->negated);
}
}
@@ -190,7 +185,7 @@ class SearchRunner
*/
protected function applyTermSearch(EloquentBuilder $entityQuery, SearchOptions $options, string $entityType): void
{
$terms = $options->searches;
$terms = $options->searches->toValueArray();
if (count($terms) === 0) {
return;
}
@@ -209,8 +204,8 @@ class SearchRunner
$subQuery->where('entity_type', '=', $entityType);
$subQuery->where(function (Builder $query) use ($terms) {
foreach ($terms as $inputTerm) {
$inputTerm = str_replace('\\', '\\\\', $inputTerm);
$query->orWhere('term', 'like', $inputTerm . '%');
$escapedTerm = str_replace('\\', '\\\\', $inputTerm);
$query->orWhere('term', 'like', $escapedTerm . '%');
}
});
$subQuery->groupBy('entity_type', 'entity_id');
@@ -264,7 +259,7 @@ class SearchRunner
$whenStatements = [];
$whenBindings = [];
foreach ($options->searches as $term) {
foreach ($options->searches->toValueArray() as $term) {
$whenStatements[] = 'WHEN term LIKE ? THEN ?';
$whenBindings[] = $term . '%';
$whenBindings[] = $term;
@@ -310,179 +305,165 @@ class SearchRunner
}
/**
* Get the available query operators as a regex escaped list.
* Apply a tag search term onto an entity query.
*/
protected function getRegexEscapedOperators(): string
protected function applyTagSearch(EloquentBuilder $query, TagSearchOption $option): void
{
$escapedOperators = [];
foreach ($this->queryOperators as $operator) {
$escapedOperators[] = preg_quote($operator);
}
$filter = function (EloquentBuilder $query) use ($option): void {
$tagParts = $option->getParts();
if (empty($tagParts['operator']) || empty($tagParts['value'])) {
$query->where('name', '=', $tagParts['name']);
return;
}
return implode('|', $escapedOperators);
if (!empty($tagParts['name'])) {
$query->where('name', '=', $tagParts['name']);
}
if (is_numeric($tagParts['value']) && $tagParts['operator'] !== 'like') {
// We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
// search the value as a string which prevents being able to do number-based operations
// on the tag values. We ensure it has a numeric value and then cast it just to be sure.
/** @var Connection $connection */
$connection = $query->getConnection();
$quotedValue = (float) trim($connection->getPdo()->quote($tagParts['value']), "'");
$query->whereRaw("value {$tagParts['operator']} {$quotedValue}");
} else if ($tagParts['operator'] === 'like') {
$query->where('value', $tagParts['operator'], str_replace('\\', '\\\\', $tagParts['value']));
} else {
$query->where('value', $tagParts['operator'], $tagParts['value']);
}
};
$option->negated ? $query->whereDoesntHave('tags', $filter) : $query->whereHas('tags', $filter);
}
/**
* Apply a tag search term onto a entity query.
*/
protected function applyTagSearch(EloquentBuilder $query, string $tagTerm): EloquentBuilder
protected function applyNegatableWhere(EloquentBuilder $query, bool $negated, string $column, string $operator, mixed $value): void
{
preg_match('/^(.*?)((' . $this->getRegexEscapedOperators() . ')(.*?))?$/', $tagTerm, $tagSplit);
$query->whereHas('tags', function (EloquentBuilder $query) use ($tagSplit) {
$tagName = $tagSplit[1];
$tagOperator = count($tagSplit) > 2 ? $tagSplit[3] : '';
$tagValue = count($tagSplit) > 3 ? $tagSplit[4] : '';
$validOperator = in_array($tagOperator, $this->queryOperators);
if (!empty($tagOperator) && !empty($tagValue) && $validOperator) {
if (!empty($tagName)) {
$query->where('name', '=', $tagName);
}
if (is_numeric($tagValue) && $tagOperator !== 'like') {
// We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will
// search the value as a string which prevents being able to do number-based operations
// on the tag values. We ensure it has a numeric value and then cast it just to be sure.
/** @var Connection $connection */
$connection = $query->getConnection();
$tagValue = (float) trim($connection->getPdo()->quote($tagValue), "'");
$query->whereRaw("value {$tagOperator} {$tagValue}");
} else {
if ($tagOperator === 'like') {
$tagValue = str_replace('\\', '\\\\', $tagValue);
}
$query->where('value', $tagOperator, $tagValue);
}
} else {
$query->where('name', '=', $tagName);
}
});
return $query;
if ($negated) {
$query->whereNot($column, $operator, $value);
} else {
$query->where($column, $operator, $value);
}
}
/**
* Custom entity search filters.
*/
protected function filterUpdatedAfter(EloquentBuilder $query, Entity $model, $input): void
protected function filterUpdatedAfter(EloquentBuilder $query, Entity $model, string $input, bool $negated): void
{
try {
$date = date_create($input);
$query->where('updated_at', '>=', $date);
} catch (\Exception $e) {
}
$date = date_create($input);
$this->applyNegatableWhere($query, $negated, 'updated_at', '>=', $date);
}
protected function filterUpdatedBefore(EloquentBuilder $query, Entity $model, $input): void
protected function filterUpdatedBefore(EloquentBuilder $query, Entity $model, string $input, bool $negated): void
{
try {
$date = date_create($input);
$query->where('updated_at', '<', $date);
} catch (\Exception $e) {
}
$date = date_create($input);
$this->applyNegatableWhere($query, $negated, 'updated_at', '<', $date);
}
protected function filterCreatedAfter(EloquentBuilder $query, Entity $model, $input): void
protected function filterCreatedAfter(EloquentBuilder $query, Entity $model, string $input, bool $negated): void
{
try {
$date = date_create($input);
$query->where('created_at', '>=', $date);
} catch (\Exception $e) {
}
$date = date_create($input);
$this->applyNegatableWhere($query, $negated, 'created_at', '>=', $date);
}
protected function filterCreatedBefore(EloquentBuilder $query, Entity $model, $input)
protected function filterCreatedBefore(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
try {
$date = date_create($input);
$query->where('created_at', '<', $date);
} catch (\Exception $e) {
}
$date = date_create($input);
$this->applyNegatableWhere($query, $negated, 'created_at', '<', $date);
}
protected function filterCreatedBy(EloquentBuilder $query, Entity $model, $input)
protected function filterCreatedBy(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$userSlug = $input === 'me' ? user()->slug : trim($input);
$user = User::query()->where('slug', '=', $userSlug)->first(['id']);
if ($user) {
$query->where('created_by', '=', $user->id);
$this->applyNegatableWhere($query, $negated, 'created_by', '=', $user->id);
}
}
protected function filterUpdatedBy(EloquentBuilder $query, Entity $model, $input)
protected function filterUpdatedBy(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$userSlug = $input === 'me' ? user()->slug : trim($input);
$user = User::query()->where('slug', '=', $userSlug)->first(['id']);
if ($user) {
$query->where('updated_by', '=', $user->id);
$this->applyNegatableWhere($query, $negated, 'updated_by', '=', $user->id);
}
}
protected function filterOwnedBy(EloquentBuilder $query, Entity $model, $input)
protected function filterOwnedBy(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$userSlug = $input === 'me' ? user()->slug : trim($input);
$user = User::query()->where('slug', '=', $userSlug)->first(['id']);
if ($user) {
$query->where('owned_by', '=', $user->id);
$this->applyNegatableWhere($query, $negated, 'owned_by', '=', $user->id);
}
}
protected function filterInName(EloquentBuilder $query, Entity $model, $input)
protected function filterInName(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$query->where('name', 'like', '%' . $input . '%');
$this->applyNegatableWhere($query, $negated, 'name', 'like', '%' . $input . '%');
}
protected function filterInTitle(EloquentBuilder $query, Entity $model, $input)
protected function filterInTitle(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$this->filterInName($query, $model, $input);
$this->filterInName($query, $model, $input, $negated);
}
protected function filterInBody(EloquentBuilder $query, Entity $model, $input)
protected function filterInBody(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$query->where($model->textField, 'like', '%' . $input . '%');
$this->applyNegatableWhere($query, $negated, $model->textField, 'like', '%' . $input . '%');
}
protected function filterIsRestricted(EloquentBuilder $query, Entity $model, $input)
protected function filterIsRestricted(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$query->whereHas('permissions');
$negated ? $query->whereDoesntHave('permissions') : $query->whereHas('permissions');
}
protected function filterViewedByMe(EloquentBuilder $query, Entity $model, $input)
protected function filterViewedByMe(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$query->whereHas('views', function ($query) {
$filter = function ($query) {
$query->where('user_id', '=', user()->id);
});
};
$negated ? $query->whereDoesntHave('views', $filter) : $query->whereHas('views', $filter);
}
protected function filterNotViewedByMe(EloquentBuilder $query, Entity $model, $input)
protected function filterNotViewedByMe(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$query->whereDoesntHave('views', function ($query) {
$filter = function ($query) {
$query->where('user_id', '=', user()->id);
});
};
$negated ? $query->whereHas('views', $filter) : $query->whereDoesntHave('views', $filter);
}
protected function filterIsTemplate(EloquentBuilder $query, Entity $model, $input)
protected function filterIsTemplate(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
if ($model instanceof Page) {
$query->where('template', '=', true);
$this->applyNegatableWhere($query, $negated, 'template', '=', true);
}
}
protected function filterSortBy(EloquentBuilder $query, Entity $model, $input)
protected function filterSortBy(EloquentBuilder $query, Entity $model, string $input, bool $negated)
{
$functionName = Str::camel('sort_by_' . $input);
if (method_exists($this, $functionName)) {
$this->$functionName($query, $model);
$this->$functionName($query, $model, $negated);
}
}
/**
* Sorting filter options.
*/
protected function sortByLastCommented(EloquentBuilder $query, Entity $model)
protected function sortByLastCommented(EloquentBuilder $query, Entity $model, bool $negated)
{
$commentsTable = DB::getTablePrefix() . 'comments';
$morphClass = str_replace('\\', '\\\\', $model->getMorphClass());
$commentQuery = DB::raw('(SELECT c1.entity_id, c1.entity_type, c1.created_at as last_commented FROM ' . $commentsTable . ' c1 LEFT JOIN ' . $commentsTable . ' c2 ON (c1.entity_id = c2.entity_id AND c1.entity_type = c2.entity_type AND c1.created_at < c2.created_at) WHERE c1.entity_type = \'' . $morphClass . '\' AND c2.created_at IS NULL) as comments');
$query->join($commentQuery, $model->getTable() . '.id', '=', 'comments.entity_id')->orderBy('last_commented', 'desc');
$query->join($commentQuery, $model->getTable() . '.id', '=', DB::raw('comments.entity_id'))
->orderBy('last_commented', $negated ? 'asc' : 'desc');
}
}

View File

@@ -9,8 +9,6 @@ use Illuminate\Http\Request;
class SettingController extends Controller
{
protected array $settingCategories = ['features', 'customization', 'registration'];
/**
* Handle requests to the settings index path.
*/
@@ -31,7 +29,7 @@ class SettingController extends Controller
// Get application version
$version = trim(file_get_contents(base_path('version')));
return view('settings.' . $category, [
return view('settings.categories.' . $category, [
'category' => $category,
'version' => $version,
'guestUser' => User::getGuest(),
@@ -59,7 +57,7 @@ class SettingController extends Controller
protected function ensureCategoryExists(string $category): void
{
if (!in_array($category, $this->settingCategories)) {
if (!view()->exists('settings.categories.' . $category)) {
abort(404);
}
}

View File

@@ -60,6 +60,7 @@ class LocaleManager
'sq' => 'sq_AL',
'sr' => 'sr_RS',
'sv' => 'sv_SE',
'tk' => 'tk_TM',
'tr' => 'tr_TR',
'uk' => 'uk_UA',
'uz' => 'uz_UZ',

View File

@@ -171,16 +171,16 @@ class AttachmentApiController extends ApiController
{
return [
'create' => [
'name' => ['required', 'min:1', 'max:255', 'string'],
'name' => ['required', 'string', 'min:1', 'max:255'],
'uploaded_to' => ['required', 'integer', 'exists:pages,id'],
'file' => array_merge(['required_without:link'], $this->attachmentService->getFileValidationRules()),
'link' => ['required_without:file', 'min:1', 'max:2000', 'safe_url'],
'link' => ['required_without:file', 'string', 'min:1', 'max:2000', 'safe_url'],
],
'update' => [
'name' => ['min:1', 'max:255', 'string'],
'name' => ['string', 'min:1', 'max:255'],
'uploaded_to' => ['integer', 'exists:pages,id'],
'file' => $this->attachmentService->getFileValidationRules(),
'link' => ['min:1', 'max:2000', 'safe_url'],
'link' => ['string', 'min:1', 'max:2000', 'safe_url'],
],
];
}

View File

@@ -166,7 +166,7 @@ class ImageRepo
*/
public function updateImageFile(Image $image, UploadedFile $file): void
{
if ($file->getClientOriginalExtension() !== pathinfo($image->path, PATHINFO_EXTENSION)) {
if (strtolower($file->getClientOriginalExtension()) !== strtolower(pathinfo($image->path, PATHINFO_EXTENSION))) {
throw new ImageUploadException(trans('errors.image_upload_replace_type'));
}

View File

@@ -7,11 +7,13 @@ use Exception;
use GuzzleHttp\Psr7\Utils;
use Illuminate\Support\Facades\Cache;
use Intervention\Image\Decoders\BinaryImageDecoder;
use Intervention\Image\Drivers\Gd\Decoders\NativeObjectDecoder;
use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\Encoders\AutoEncoder;
use Intervention\Image\Encoders\PngEncoder;
use Intervention\Image\Interfaces\ImageInterface as InterventionImage;
use Intervention\Image\ImageManager;
use Intervention\Image\Origin;
class ImageResizer
{
@@ -99,7 +101,7 @@ class ImageResizer
}
// If not in cache and thumbnail does not exist, generate thumb and cache path
$thumbData = $this->resizeImageData($imageData, $width, $height, $keepRatio);
$thumbData = $this->resizeImageData($imageData, $width, $height, $keepRatio, $this->getExtension($image));
$disk->put($thumbFilePath, $thumbData, true);
Cache::put($thumbCacheKey, $thumbFilePath, static::THUMBNAIL_CACHE_TIME);
@@ -120,7 +122,7 @@ class ImageResizer
?string $format = null,
): string {
try {
$thumb = $this->interventionFromImageData($imageData);
$thumb = $this->interventionFromImageData($imageData, $format);
} catch (Exception $e) {
throw new ImageUploadException(trans('errors.cannot_create_thumbs'));
}
@@ -154,11 +156,23 @@ class ImageResizer
* Performs some manual library usage to ensure image is specifically loaded
* from given binary data instead of data being misinterpreted.
*/
protected function interventionFromImageData(string $imageData): InterventionImage
protected function interventionFromImageData(string $imageData, ?string $fileType): InterventionImage
{
$manager = new ImageManager(new Driver());
return $manager->read($imageData, BinaryImageDecoder::class);
// Ensure gif images are decoded natively instead of deferring to intervention GIF
// handling since we don't need the added animation support.
$isGif = $fileType === 'gif';
$decoder = $isGif ? NativeObjectDecoder::class : BinaryImageDecoder::class;
$input = $isGif ? @imagecreatefromstring($imageData) : $imageData;
$image = $manager->read($input, $decoder);
if ($isGif) {
$image->setOrigin(new Origin('image/gif'));
}
return $image;
}
/**
@@ -209,7 +223,15 @@ class ImageResizer
*/
protected function isGif(Image $image): bool
{
return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif';
return $this->getExtension($image) === 'gif';
}
/**
* Get the extension for the given image, normalised to lower-case.
*/
protected function getExtension(Image $image): string
{
return strtolower(pathinfo($image->path, PATHINFO_EXTENSION));
}
/**

View File

@@ -21,7 +21,7 @@ class RoleApiController extends ApiController
'display_name' => ['required', 'string', 'min:3', 'max:180'],
'description' => ['string', 'max:180'],
'mfa_enforced' => ['boolean'],
'external_auth_id' => ['string'],
'external_auth_id' => ['string', 'max:180'],
'permissions' => ['array'],
'permissions.*' => ['string'],
],
@@ -29,7 +29,7 @@ class RoleApiController extends ApiController
'display_name' => ['string', 'min:3', 'max:180'],
'description' => ['string', 'max:180'],
'mfa_enforced' => ['boolean'],
'external_auth_id' => ['string'],
'external_auth_id' => ['string', 'max:180'],
'permissions' => ['array'],
'permissions.*' => ['string'],
]

View File

@@ -75,7 +75,7 @@ class RoleController extends Controller
$data = $this->validate($request, [
'display_name' => ['required', 'min:3', 'max:180'],
'description' => ['max:180'],
'external_auth_id' => ['string'],
'external_auth_id' => ['string', 'max:180'],
'permissions' => ['array'],
'mfa_enforced' => ['string'],
]);
@@ -109,7 +109,7 @@ class RoleController extends Controller
$data = $this->validate($request, [
'display_name' => ['required', 'min:3', 'max:180'],
'description' => ['max:180'],
'external_auth_id' => ['string'],
'external_auth_id' => ['string', 'max:180'],
'permissions' => ['array'],
'mfa_enforced' => ['string'],
]);

View File

@@ -37,27 +37,28 @@ class UserApiController extends ApiController
{
return [
'create' => [
'name' => ['required', 'min:2', 'max:100'],
'name' => ['required', 'string', 'min:1', 'max:100'],
'email' => [
'required', 'min:2', 'email', new Unique('users', 'email'),
'required', 'string', 'email', 'min:2', new Unique('users', 'email'),
],
'external_auth_id' => ['string'],
'language' => ['string', 'max:15', 'alpha_dash'],
'password' => [Password::default()],
'password' => ['string', Password::default()],
'roles' => ['array'],
'roles.*' => ['integer'],
'send_invite' => ['boolean'],
],
'update' => [
'name' => ['min:2', 'max:100'],
'name' => ['string', 'min:1', 'max:100'],
'email' => [
'min:2',
'string',
'email',
'min:2',
(new Unique('users', 'email'))->ignore($userId ?? null),
],
'external_auth_id' => ['string'],
'language' => ['string', 'max:15', 'alpha_dash'],
'password' => [Password::default()],
'password' => ['string', Password::default()],
'roles' => ['array'],
'roles.*' => ['integer'],
],

View File

@@ -3,6 +3,7 @@
namespace BookStack\Users\Controllers;
use BookStack\Access\SocialDriverManager;
use BookStack\Access\UserInviteException;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Exceptions\UserUpdateException;
use BookStack\Http\Controller;
@@ -14,6 +15,7 @@ use BookStack\Util\SimpleListOptions;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rules\Password;
use Illuminate\Validation\ValidationException;
@@ -91,9 +93,15 @@ class UserController extends Controller
$validated = $this->validate($request, array_filter($validationRules));
DB::transaction(function () use ($validated, $sendInvite) {
$this->userRepo->create($validated, $sendInvite);
});
try {
DB::transaction(function () use ($validated, $sendInvite) {
$this->userRepo->create($validated, $sendInvite);
});
} catch (UserInviteException $e) {
Log::error("Failed to send user invite with error: {$e->getMessage()}");
$this->showErrorNotification(trans('errors.users_could_not_send_invite'));
return redirect('/settings/users/create')->withInput();
}
return redirect('/settings/users');
}
@@ -136,7 +144,7 @@ class UserController extends Controller
$this->checkPermission('users-manage');
$validated = $this->validate($request, [
'name' => ['min:2', 'max:100'],
'name' => ['min:1', 'max:100'],
'email' => ['min:2', 'email', 'unique:users,email,' . $id],
'password' => ['required_with:password_confirm', Password::default()],
'password-confirm' => ['same:password', 'required_with:password'],

View File

@@ -2,6 +2,7 @@
namespace BookStack\Users;
use BookStack\Access\UserInviteException;
use BookStack\Access\UserInviteService;
use BookStack\Activity\ActivityType;
use BookStack\Entities\EntityProvider;
@@ -83,6 +84,7 @@ class UserRepo
* As per "createWithoutActivity" but records a "create" activity.
*
* @param array{name: string, email: string, password: ?string, external_auth_id: ?string, language: ?string, roles: ?array} $data
* @throws UserInviteException
*/
public function create(array $data, bool $sendInvite = false): User
{

View File

@@ -133,18 +133,30 @@ class CspService
protected function getAllowedIframeSources(): array
{
$sources = config('app.iframe_sources', '');
$hosts = array_filter(explode(' ', $sources));
$sources = explode(' ', config('app.iframe_sources', ''));
$sources[] = $this->getDrawioHost();
// Extract drawing service url to allow embedding if active
return array_filter($sources);
}
/**
* Extract the host name of the configured drawio URL for use in CSP.
* Returns empty string if not in use.
*/
protected function getDrawioHost(): string
{
$drawioConfigValue = config('services.drawio');
if ($drawioConfigValue) {
$drawioSource = is_string($drawioConfigValue) ? $drawioConfigValue : 'https://embed.diagrams.net/';
$drawioSourceParsed = parse_url($drawioSource);
$drawioHost = $drawioSourceParsed['scheme'] . '://' . $drawioSourceParsed['host'];
$hosts[] = $drawioHost;
if (!$drawioConfigValue) {
return '';
}
return $hosts;
$drawioSource = is_string($drawioConfigValue) ? $drawioConfigValue : 'https://embed.diagrams.net/';
$drawioSourceParsed = parse_url($drawioSource);
$drawioHost = $drawioSourceParsed['scheme'] . '://' . $drawioSourceParsed['host'];
if (isset($drawioSourceParsed['port'])) {
$drawioHost .= ':' . $drawioSourceParsed['port'];
}
return $drawioHost;
}
}

View File

@@ -6,6 +6,7 @@ use DOMDocument;
use DOMElement;
use DOMNode;
use DOMNodeList;
use DOMText;
use DOMXPath;
/**
@@ -81,6 +82,14 @@ class HtmlDocument
return $element;
}
/**
* Create a new text node within this document.
*/
public function createTextNode(string $text): DOMText
{
return $this->document->createTextNode($text);
}
/**
* Get an element within the document of the given ID.
*/

Binary file not shown.

View File

@@ -16,13 +16,13 @@
"ext-json": "*",
"ext-mbstring": "*",
"ext-xml": "*",
"bacon/bacon-qr-code": "^2.0",
"bacon/bacon-qr-code": "^3.0",
"doctrine/dbal": "^3.5",
"dompdf/dompdf": "^2.0",
"dompdf/dompdf": "^3.0",
"guzzlehttp/guzzle": "^7.4",
"intervention/image": "^3.5",
"knplabs/knp-snappy": "^1.5",
"laravel/framework": "^10.10",
"laravel/framework": "^10.48.23",
"laravel/socialite": "^5.10",
"laravel/tinker": "^2.8",
"league/commonmark": "^2.3",

1694
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// Ensure we have an "editor" value set for pages
// Get default
$default = DB::table('settings')
->where('setting_key', '=', 'app-editor')
->first()
->value ?? 'wysiwyg';
$default = ($default === 'markdown') ? 'markdown' : 'wysiwyg';
// We set it to 'markdown' for pages currently with markdown content
DB::table('pages')
->where('editor', '=', '')
->where('markdown', '!=', '')
->update(['editor' => 'markdown']);
// We set it to 'wysiwyg' where we have HTML but no markdown
DB::table('pages')
->where('editor', '=', '')
->where('markdown', '=', '')
->where('html', '!=', '')
->update(['editor' => 'wysiwyg']);
// Otherwise, where still empty, set to the current default
DB::table('pages')
->where('editor', '=', '')
->update(['editor' => $default]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Can't reverse due to not knowing what would have been empty before
}
};

View File

@@ -8,7 +8,6 @@
"updated_at": "2022-02-03T16:27:55.000000Z",
"external_auth_id": "abc123456",
"slug": "dan-brown",
"user_id": 1,
"last_activity_at": "2022-02-03T16:27:55.000000Z",
"profile_url": "https://docs.example.com/user/dan-brown",
"edit_url": "https://docs.example.com/settings/users/1",
@@ -22,7 +21,6 @@
"updated_at": "2021-11-18T17:10:58.000000Z",
"external_auth_id": "",
"slug": "benny",
"user_id": 2,
"last_activity_at": "2022-01-31T20:39:24.000000Z",
"profile_url": "https://docs.example.com/user/benny",
"edit_url": "https://docs.example.com/settings/users/2",

View File

@@ -14,6 +14,7 @@ const entryPoints = {
code: path.join(__dirname, '../../resources/js/code/index.mjs'),
'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'),
markdown: path.join(__dirname, '../../resources/js/markdown/index.mjs'),
wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.ts'),
};
// Locate our output directory
@@ -31,6 +32,15 @@ esbuild.build({
format: 'esm',
minify: isProd,
logLevel: 'info',
loader: {
'.svg': 'text',
},
absWorkingDir: path.join(__dirname, '../..'),
alias: {
'@icons': './resources/icons',
lexical: './resources/js/wysiwyg/lexical/core',
'@lexical': './resources/js/wysiwyg/lexical',
},
banner: {
js: '// See the "/licenses" URI for full package license details',
css: '/* See the "/licenses" URI for full package license details */',

View File

@@ -1,4 +1,4 @@
FROM php:8.1-apache
FROM php:8.3-apache
ENV APACHE_DOCUMENT_ROOT /app/public
WORKDIR /app

View File

@@ -137,8 +137,8 @@ window.$events.showValidationErrors(error);
// Translator
// Take the given plural text and count to decide on what plural option
// to use, Similar to laravel's trans_choice function but instead
// takes the direction directly instead of a translation key.
window.trans_plural(translationString, count, replacements);
// takes the translation text directly instead of a translation key.
window.$trans.choice(translationString, count, replacements);
// Component System
// Parse and initialise any components from the given root el down.

View File

@@ -6,14 +6,14 @@ This theme system itself is maintained and supported but usages of this system,
## Getting Started
*[Video Guide](https://www.youtube.com/watch?v=gLy_2GBse48)*
*[Video Guide](https://foss.video/w/ibNY6bGmKFV1tva3Jz4KfA)*
This makes use of the theme system. Create a folder for your theme within your BookStack `themes` directory. As an example we'll use `my_theme`, so we'd create a `themes/my_theme` folder.
You'll need to tell BookStack to use your theme via the `APP_THEME` option in your `.env` file. For example: `APP_THEME=my_theme`.
## Customizing View Files
Content placed in your `themes/<theme_name>/` folder will override the original view files found in the `resources/views` folder. These files are typically [Laravel Blade](https://laravel.com/docs/8.x/blade) files.
Content placed in your `themes/<theme_name>/` folder will override the original view files found in the `resources/views` folder. These files are typically [Laravel Blade](https://laravel.com/docs/10.x/blade) files.
As an example, I could override the `resources/views/books/parts/list-item.blade.php` file with my own template at the path `themes/<theme_name>/books/parts/list-item.blade.php`.
## Customizing Icons
@@ -22,7 +22,7 @@ SVG files placed in a `themes/<theme_name>/icons` folder will override any icons
## Customizing Text Content
Folders with PHP translation files placed in a `themes/<theme_name>/lang` folder will override translations defined within the `resources/lang` folder. Custom translations are merged with the original files so you only need to override the select translations you want to override, you don't need to copy the whole original file. Note that you'll need to include the language folder.
Folders with PHP translation files placed in a `themes/<theme_name>/lang` folder will override translations defined within the `lang` folder. Custom translations are merged with the original files, so you only need to override the select translations you want to override, you don't need to copy the whole original file. Note that you'll need to include the language folder.
As an example, Say I wanted to change 'Search' to 'Find'; Within a `themes/<theme_name>/lang/en/common.php` file I'd set the following:

File diff suppressed because it is too large Load Diff

View File

@@ -96,6 +96,22 @@ Copyright: Copyright (C) 1991, 1999 Free Software Foundation, Inc.
Source: https://github.com/dompdf/dompdf.git
Link: https://github.com/dompdf/dompdf
-----------
dompdf/php-font-lib
License: LGPL-2.1-or-later
License File: vendor/dompdf/php-font-lib/LICENSE
Copyright: Copyright (C) 1991, 1999 Free Software Foundation, Inc.
Source: https://github.com/dompdf/php-font-lib.git
Link: https://github.com/dompdf/php-font-lib
-----------
dompdf/php-svg-lib
License: LGPL-3.0-or-later
License File: vendor/dompdf/php-svg-lib/LICENSE
Copyright: Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Source: https://github.com/dompdf/php-svg-lib.git
Link: https://github.com/dompdf/php-svg-lib
-----------
dragonmantank/cron-expression
License: MIT
License File: vendor/dragonmantank/cron-expression/LICENSE
@@ -128,7 +144,7 @@ Link: https://github.com/fruitcake/php-cors
graham-campbell/result-type
License: MIT
License File: vendor/graham-campbell/result-type/LICENSE
Copyright: Copyright (c) 2020-2023 Graham Campbell <*****@**********.**.**>
Copyright: Copyright (c) 2020-2024 Graham Campbell <*****@**********.**.**>
Source: https://github.com/GrahamCampbell/Result-Type.git
Link: https://github.com/GrahamCampbell/Result-Type.git
-----------
@@ -357,9 +373,9 @@ Link: https://github.com/nunomaduro/termwind.git
onelogin/php-saml
License: MIT
License File: vendor/onelogin/php-saml/LICENSE
Copyright: Copyright (c) 2010-2016 OneLogin, Inc.
Source: https://github.com/onelogin/php-saml.git
Link: https://developers.onelogin.com/saml/php
Copyright: Copyright (c) 2010-2022 OneLogin, Inc.
Source: https://github.com/SAML-Toolkits/php-saml.git
Link: https://github.com/SAML-Toolkits/php-saml
-----------
paragonie/constant_time_encoding
License: MIT
@@ -375,22 +391,6 @@ Copyright: Copyright (c) 2015 Paragon Initiative Enterprises
Source: https://github.com/paragonie/random_compat.git
Link: https://github.com/paragonie/random_compat.git
-----------
phenx/php-font-lib
License: LGPL-2.1-or-later
License File: vendor/phenx/php-font-lib/LICENSE
Copyright: Copyright (C) 1991, 1999 Free Software Foundation, Inc.
Source: https://github.com/dompdf/php-font-lib.git
Link: https://github.com/PhenX/php-font-lib
-----------
phenx/php-svg-lib
License: LGPL-3.0-or-later
License File: vendor/phenx/php-svg-lib/LICENSE
Copyright: Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Source: https://github.com/dompdf/php-svg-lib.git
Link: https://github.com/PhenX/php-svg-lib
-----------
phpoption/phpoption
License: Apache-2.0
License File: vendor/phpoption/phpoption/LICENSE
@@ -415,7 +415,7 @@ predis/predis
License: MIT
License File: vendor/predis/predis/LICENSE
Copyright: Copyright (c) 2009-2020 Daniele Alessandri (original work)
Copyright (c) 2021-2023 Till Krüss (modified work)
Copyright (c) 2021-2024 Till Krüss (modified work)
Source: https://github.com/predis/predis.git
Link: http://github.com/predis/predis
-----------
@@ -514,7 +514,7 @@ Link: https://github.com/ramsey/uuid.git
robrichards/xmlseclibs
License: BSD-3-Clause
License File: vendor/robrichards/xmlseclibs/LICENSE
Copyright: Copyright (c) 2007-2019, Robert Richards <*********@*********.***>.
Copyright: Copyright (c) 2007-2024, Robert Richards <*********@*********.***>.
Source: https://github.com/robrichards/xmlseclibs.git
Link: https://github.com/robrichards/xmlseclibs
-----------
@@ -676,13 +676,6 @@ Copyright: Copyright (c) 2015-present Fabien Potencier
Source: https://github.com/symfony/polyfill-mbstring.git
Link: https://symfony.com
-----------
symfony/polyfill-php72
License: MIT
License File: vendor/symfony/polyfill-php72/LICENSE
Copyright: Copyright (c) 2015-present Fabien Potencier
Source: https://github.com/symfony/polyfill-php72.git
Link: https://symfony.com
-----------
symfony/polyfill-php80
License: MIT
License File: vendor/symfony/polyfill-php80/LICENSE

View File

@@ -1,20 +1,17 @@
# This is a Docker Compose configuration
# intended for development purposes only
version: '3'
volumes:
db: {}
services:
db:
image: mysql:8
image: mysql:8.4
environment:
MYSQL_DATABASE: bookstack-dev
MYSQL_USER: bookstack-test
MYSQL_PASSWORD: bookstack-test
MYSQL_RANDOM_ROOT_PASSWORD: 'true'
command: --default-authentication-plugin=mysql_native_password
volumes:
- ./dev/docker/init.db:/docker-entrypoint-initdb.d
- db:/var/lib/mysql
@@ -43,7 +40,7 @@ services:
extra_hosts:
- "host.docker.internal:host-gateway"
node:
image: node:alpine
image: node:22-alpine
working_dir: /app
user: node
volumes:

209
jest.config.ts Normal file
View File

@@ -0,0 +1,209 @@
/**
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/
import type {Config} from 'jest';
import {pathsToModuleNameMapper} from "ts-jest";
import { compilerOptions } from './tsconfig.json';
const config: Config = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/tmp/jest_rs",
// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// The default configuration for fake timers
// fakeTimers: {
// "enableGlobally": false
// },
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
globals: {
__DEV__: true,
},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "mjs",
// "cjs",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
modulePaths: ['./'],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
'lexical/shared/invariant': 'resources/js/wysiwyg/lexical/core/shared/__mocks__/invariant',
...pathsToModuleNameMapper(compilerOptions.paths),
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: undefined,
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state before every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
roots: [
"./resources/js"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "jsdom",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"**/__tests__/**/*.test.[jt]s",
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
// A map from regular expressions to paths to transformers
transform: {
"^.+.tsx?$": ["ts-jest",{}],
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/",
// "\\.pnp\\.[^\\/]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
export default config;

View File

@@ -89,9 +89,9 @@ return [
'mfa_setup_action' => 'إعداد (تنصيب)',
'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.',
'mfa_option_totp_title' => 'تطبيق الجوال',
'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Backup Codes',
'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.',
'mfa_option_totp_desc' => 'لاستخدام المصادقة المتعددة العوامل، ستحتاج إلى تطبيق محمول يدعم TOTP مثل Google Authenticator أو Authy أو Microsoft Authenticer.',
'mfa_option_backup_codes_title' => 'رموز النسخ الاحتياطي',
'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.',
'mfa_gen_confirm_and_enable' => 'Confirm and Enable',
'mfa_gen_backup_codes_title' => 'Backup Codes Setup',
'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.',

View File

@@ -20,7 +20,7 @@ return [
'description' => 'الوصف',
'role' => 'الدور',
'cover_image' => 'صورة الغلاف',
'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.',
'cover_image_description' => 'يجب أن يكون حجم هذه الصورة تقريبًا 440x250 بكسل، على الرغم من أنه سيتم تحجيمها وقصها بشكل مرن لتناسب واجهة المستخدم في سيناريوهات مختلفة حسب الحاجة، لذا فإن الأبعاد الفعلية للعرض ستختلف.',
// Actions
'actions' => 'إجراءات',
@@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'سياسة الخصوصية',
'terms_of_service' => 'اتفاقية شروط الخدمة',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@@ -224,6 +224,8 @@ return [
'pages_edit_switch_to_markdown_clean' => '(Clean Content)',
'pages_edit_switch_to_markdown_stable' => '(Stable Content)',
'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Alpha Testing)',
'pages_edit_set_changelog' => 'تثبيت سجل التعديل',
'pages_edit_enter_changelog_desc' => 'ضع وصف مختصر للتعديلات التي تمت',
'pages_edit_enter_changelog' => 'أدخل سجل التعديل',

View File

@@ -37,6 +37,7 @@ return [
'social_driver_not_found' => 'لم يتم العثور على السوشيال درايفر "Social driver"',
'social_driver_not_configured' => 'لم يتم تهيئة إعدادات حسابك الاجتماعي بشكل صحيح.',
'invite_token_expired' => 'انتهت صلاحية رابط هذه الدعوة. يمكنك بدلاً من ذلك محاولة إعادة تعيين كلمة مرور حسابك.',
'login_user_not_found' => 'A user for this action could not be found.',
// System
'path_not_writable' => 'لا يمكن الرفع إلى مسار :filePath. الرجاء التأكد من قابلية الكتابة إلى الخادم.',
@@ -77,6 +78,7 @@ return [
// Users
'users_cannot_delete_only_admin' => 'لا يمكن حذف المشرف الوحيد',
'users_cannot_delete_guest' => 'لا يمكن حذف المستخدم الضيف',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send',
// Roles
'role_cannot_be_edited' => 'لا يمكن تعديل هذا الدور',

View File

@@ -295,6 +295,7 @@ return [
'bs' => 'Bosanski',
'ca' => 'Català',
'cs' => 'Česky',
'cy' => 'Cymraeg',
'da' => 'Dansk',
'de' => 'Deutsch (Sie)',
'de_informal' => 'Deutsch (Du)',

View File

@@ -91,7 +91,7 @@ return [
'mfa_option_totp_title' => 'Мобилно приложение',
'mfa_option_totp_desc' => 'За да използваш многофакторно удостоверяване, ще ти трябва мобилно приложение, което поддържа временни еднократни пароли (TOTP), като например Google Authenticator, Authy или Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Резервни кодове',
'mfa_option_backup_codes_desc' => 'Запази на сигурно място набор от еднократни резервни кодове, с които можеш да устовериш идентичността си.',
'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.',
'mfa_gen_confirm_and_enable' => 'Потвърди и включи',
'mfa_gen_backup_codes_title' => 'Настройка на резервни кодове',
'mfa_gen_backup_codes_desc' => 'Запази този лист с кодове на сигурно място. Когато достъпваш системата, ще можеш да използваш един от тези кодове като вторичен механизъм за удостоверяване.',

View File

@@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Политика за поверителност',
'terms_of_service' => 'Условия на услугата',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@@ -224,6 +224,8 @@ return [
'pages_edit_switch_to_markdown_clean' => '(Clean Content)',
'pages_edit_switch_to_markdown_stable' => '(Stable Content)',
'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Alpha Testing)',
'pages_edit_set_changelog' => 'Задайте регистър на промените',
'pages_edit_enter_changelog_desc' => 'Въведете кратко резюме на промените, които сте създали',
'pages_edit_enter_changelog' => 'Въведи регистър на промените',

View File

@@ -37,6 +37,7 @@ return [
'social_driver_not_found' => 'Кодът за връзка със социалната мрежа не съществува',
'social_driver_not_configured' => 'Социалните настройки на твоя :socialAccount не са конфигурирани правилно.',
'invite_token_expired' => 'Твоята покана е изтекла. Вместо това може да пробваш да възстановиш паролата на профила си.',
'login_user_not_found' => 'A user for this action could not be found.',
// System
'path_not_writable' => 'Не може да се качи файл в :filePath. Увери се на сървъра, че в пътя може да се записва.',
@@ -77,6 +78,7 @@ return [
// Users
'users_cannot_delete_only_admin' => 'Не можеш да изтриеш единствения администратор',
'users_cannot_delete_guest' => 'Не можеш да изтриеш потребителя на госта',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send',
// Roles
'role_cannot_be_edited' => 'Ролята не може да бъде редактирана',

View File

@@ -295,6 +295,7 @@ return [
'bs' => 'Bosanski',
'ca' => 'Català',
'cs' => 'Česky',
'cy' => 'Cymraeg',
'da' => 'Dansk',
'de' => 'Deutsch (Sie)',
'de_informal' => 'Deutsch (Du)',

View File

@@ -91,7 +91,7 @@ return [
'mfa_option_totp_title' => 'Mobile App',
'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Backup Codes',
'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.',
'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.',
'mfa_gen_confirm_and_enable' => 'Confirm and Enable',
'mfa_gen_backup_codes_title' => 'Backup Codes Setup',
'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.',

View File

@@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Pravila o privatnosti',
'terms_of_service' => 'Uslovi korištenja',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@@ -224,6 +224,8 @@ return [
'pages_edit_switch_to_markdown_clean' => '(Clean Content)',
'pages_edit_switch_to_markdown_stable' => '(Stable Content)',
'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Alpha Testing)',
'pages_edit_set_changelog' => 'Set Changelog',
'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made',
'pages_edit_enter_changelog' => 'Enter Changelog',

View File

@@ -37,6 +37,7 @@ return [
'social_driver_not_found' => 'Driver društvene mreže nije pronađen',
'social_driver_not_configured' => 'Vaše :socialAccount postavke nisu konfigurisane ispravno.',
'invite_token_expired' => 'Pozivni link je istekao. Možete umjesto toga pokušati da resetujete lozinku.',
'login_user_not_found' => 'A user for this action could not be found.',
// System
'path_not_writable' => 'Na putanju fajla :filePath se ne može učitati. Potvrdite da je omogućeno pisanje na server.',
@@ -77,6 +78,7 @@ return [
// Users
'users_cannot_delete_only_admin' => 'Ne možete izbrisati jedinog administratora',
'users_cannot_delete_guest' => 'Ne možete izbrisati gost korisnika',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send',
// Roles
'role_cannot_be_edited' => 'Ova uloga ne može biti mijenjana',

View File

@@ -295,6 +295,7 @@ return [
'bs' => 'Bosanski',
'ca' => 'Català',
'cs' => 'Česky',
'cy' => 'Cymraeg',
'da' => 'Dansk',
'de' => 'Deutsch (Sie)',
'de_informal' => 'Deutsch (Du)',

View File

@@ -7,116 +7,116 @@ return [
// Pages
'page_create' => 'ha creat la pàgina',
'page_create_notification' => 'Pàgina creada correctament',
'page_create_notification' => 'Sha creat la pàgina',
'page_update' => 'ha actualitzat la pàgina',
'page_update_notification' => 'Pàgina actualitzada correctament',
'page_delete' => 'ha suprimit una pàgina',
'page_delete_notification' => 'Pàgina suprimida correctament',
'page_update_notification' => 'Sha actualitzat la pàgina',
'page_delete' => 'ha suprimit la pàgina',
'page_delete_notification' => 'Sha suprimit la pàgina',
'page_restore' => 'ha restaurat la pàgina',
'page_restore_notification' => 'Pàgina restaurada correctament',
'page_restore_notification' => 'Sha restaurat la pàgina',
'page_move' => 'ha mogut la pàgina',
'page_move_notification' => 'Pàgina moguda correctament',
'page_move_notification' => 'Sha mogut la pàgina',
// Chapters
'chapter_create' => 'ha creat el capítol',
'chapter_create_notification' => 'Capítol creat correctament',
'chapter_create_notification' => 'Sha creat el capítol',
'chapter_update' => 'ha actualitzat el capítol',
'chapter_update_notification' => 'Capítol actualitzat correctament',
'chapter_delete' => 'ha suprimit un capítol',
'chapter_delete_notification' => 'Capítol suprimit correctament',
'chapter_update_notification' => 'Sha actualitzat el capítol',
'chapter_delete' => 'ha suprimit el capítol',
'chapter_delete_notification' => 'Sha suprimit el capítol',
'chapter_move' => 'ha mogut el capítol',
'chapter_move_notification' => 'Capítol mogut correctament',
'chapter_move_notification' => 'Sha mogut el capítol',
// Books
'book_create' => 'ha creat el llibre',
'book_create_notification' => 'Llibre creat correctament',
'book_create_from_chapter' => 'ha convertit un capítol en el llibre',
'book_create_from_chapter_notification' => 'Capítol convertit en llibre correctament',
'book_create_notification' => 'Sha creat el llibre',
'book_create_from_chapter' => 'ha convertit el capítol a llibre',
'book_create_from_chapter_notification' => 'Sha convertit el capítol a llibre',
'book_update' => 'ha actualitzat el llibre',
'book_update_notification' => 'Llibre actualitzat correctament',
'book_delete' => 'ha suprimit un llibre',
'book_delete_notification' => 'Llibre suprimit correctament',
'book_update_notification' => 'Sha actualitzat el llibre',
'book_delete' => 'ha suprimit el llibre',
'book_delete_notification' => 'Sha suprimit el llibre',
'book_sort' => 'ha ordenat el llibre',
'book_sort_notification' => 'Llibre reordenat correctament',
'book_sort_notification' => 'Sha tornat a ordenar el llibre',
// Bookshelves
'bookshelf_create' => 'ha creat el prestatge',
'bookshelf_create_notification' => 'Prestatge creat correctament',
'bookshelf_create_from_book' => 'ha convertit un llibre en el prestatge',
'bookshelf_create_from_book_notification' => 'Llibre convertit en prestatge correctament',
'bookshelf_create_notification' => 'Sha creat el prestatge',
'bookshelf_create_from_book' => 'ha convertit el llibre a prestatge',
'bookshelf_create_from_book_notification' => 'Sha convertit el llibre a prestatge',
'bookshelf_update' => 'ha actualitzat el prestatge',
'bookshelf_update_notification' => 'Prestatge actualitzat correctament',
'bookshelf_delete' => 'ha suprimit un prestatge',
'bookshelf_delete_notification' => 'Prestatge suprimit correctament',
'bookshelf_update_notification' => 'Sha actualitzat el prestatge',
'bookshelf_delete' => 'ha suprimit el prestatge',
'bookshelf_delete_notification' => 'Sha suprimit el prestatge',
// Revisions
'revision_restore' => 'ha restaurat la revisió',
'revision_delete' => 'ha suprimit una revisió',
'revision_delete_notification' => 'Revisió suprimida correctament',
'revision_delete' => 'ha suprimit la revisió',
'revision_delete_notification' => 'Sha suprimit la revisió',
// Favourites
'favourite_add_notification' => 'Sha afegit «:name» als vostres preferits',
'favourite_remove_notification' => 'Sha suprimit «:name» dels vostres preferits',
'favourite_add_notification' => 'Sha afegit &laquo;:name&raquo; als favorits.',
'favourite_remove_notification' => 'Sha eliminat &laquo;:name&raquo; dels favorits.',
// Watching
'watch_update_level_notification' => 'Preferències de seguiment actualitzades correctament',
'watch_update_level_notification' => 'Shan actualitzat les preferències de seguiment',
// Auth
'auth_login' => 'ha iniciat la sessió',
'auth_login' => 'ha iniciat sessió',
'auth_register' => 'sha registrat com a usuari nou',
'auth_password_reset_request' => 'ha sol·licitat un restabliment de la contrasenya',
'auth_password_reset_update' => 'ha restablert la contrasenya de lusuari',
'mfa_setup_method' => 'ha configurat un mètode dautenticació de múltiple factor',
'mfa_setup_method_notification' => 'Mètode dautenticació de múltiple factor configurat correctament',
'mfa_remove_method' => 'ha suprimit un mètode dautenticació de múltiple factor',
'mfa_remove_method_notification' => 'Mètode dautenticació de múltiple factor suprimit correctament',
'auth_password_reset_request' => 'ha sol·licitat la reinicialització de la contrasenya',
'auth_password_reset_update' => 'ha reinicialitzat la contrasenya',
'mfa_setup_method' => 'ha configurat un mètode dautenticació multifactorial',
'mfa_setup_method_notification' => 'Sha configurat un mètode dautenticació multifactorial',
'mfa_remove_method' => 'ha eliminat un mètode dautenticació multifactorial',
'mfa_remove_method_notification' => 'Sha eliminat un mètode dautenticació multifactorial',
// Settings
'settings_update' => 'ha actualitzat la configuració',
'settings_update_notification' => 'Configuració actualitzada correctament',
'settings_update_notification' => 'Sha actualitzat la configuració',
'maintenance_action_run' => 'ha executat una acció de manteniment',
// Webhooks
'webhook_create' => 'ha creat un webhook',
'webhook_create_notification' => 'Webhook creat correctament',
'webhook_update' => 'ha actualitzat un webhook',
'webhook_update_notification' => 'Webhook actualitzat correctament',
'webhook_delete' => 'ha suprimit un webhook',
'webhook_delete_notification' => 'Webhook suprimit correctament',
'webhook_create_notification' => 'Sha creat el webhook',
'webhook_update' => 'ha actualitzat el webhook',
'webhook_update_notification' => 'Sha actualitzat el webhook',
'webhook_delete' => 'ha suprimit el webhook',
'webhook_delete_notification' => 'Sha suprimit el webhook',
// Users
'user_create' => 'ha creat lusuari',
'user_create_notification' => 'Usuari creat correctament',
'user_create_notification' => 'Sha creat lusuari',
'user_update' => 'ha actualitzat lusuari',
'user_update_notification' => 'Usuari actualitzat correctament',
'user_delete' => 'ha suprimit un usuari',
'user_delete_notification' => 'Usuari suprimit correctament',
'user_update_notification' => 'Sha actualitzat lusuari',
'user_delete' => 'ha suprimit lusuari',
'user_delete_notification' => 'Sha suprimit lusuari',
// API Tokens
'api_token_create' => 'created API token',
'api_token_create_notification' => 'Testimoni dAPI creat correctament',
'api_token_update' => 'updated API token',
'api_token_update_notification' => 'Testimoni dAPI actualitzat correctament',
'api_token_delete' => 'deleted API token',
'api_token_delete_notification' => 'Testimoni dAPI suprimit correctament',
'api_token_create' => 'ha creat el testimoni API',
'api_token_create_notification' => 'Sha creat el testimoni API',
'api_token_update' => 'ha actualitzat el testimoni API',
'api_token_update_notification' => 'Sha actualitzat el testimoni API',
'api_token_delete' => 'ha suprimit el testimoni API',
'api_token_delete_notification' => 'Sha suprimit el testimoni API',
// Roles
'role_create' => 'ha creat el rol',
'role_create_notification' => 'Rol creat correctament',
'role_create_notification' => 'Sha creat el rol',
'role_update' => 'ha actualitzat el rol',
'role_update_notification' => 'Rol actualitzat correctament',
'role_delete' => 'ha suprimit un rol',
'role_delete_notification' => 'Rol suprimit correctament',
'role_update_notification' => 'Sha actualitzat el rol',
'role_delete' => 'ha suprimit el rol',
'role_delete_notification' => 'Sha suprimit el rol',
// Recycle Bin
'recycle_bin_empty' => 'ha buidat la paperera de reciclatge',
'recycle_bin_restore' => 'ha restaurat de la paperera de reciclatge',
'recycle_bin_destroy' => 'ha suprimit de la paperera de reciclatge',
'recycle_bin_empty' => 'ha buidat la paperera',
'recycle_bin_restore' => 'ha restaurat de la paperera',
'recycle_bin_destroy' => 'ha eliminat de la paperera',
// Comments
'commented_on' => 'ha comentat a',
'comment_create' => 'ha afegit el comentari',
'comment_update' => 'ha actualitzat el comentari',
'comment_create' => 'ha afegit un comentari',
'comment_update' => 'ha actualitzat un comentari',
'comment_delete' => 'ha suprimit un comentari',
// Other

View File

@@ -6,112 +6,112 @@
*/
return [
'failed' => 'Les credencials no coincideixen amb les que hi ha emmagatzemades.',
'throttle' => 'Massa intents dinici de sessió. Torneu-ho a provar daquí a :seconds segons.',
'failed' => 'Aquestes credencials no existeixen al nostre registre.',
'throttle' => 'Massa intents dinici de sessió. Torneu-ho a provar daquí :seconds segons.',
// Login & Register
'sign_up' => 'Registra-mhi',
'log_in' => 'Inicia la sessió',
'log_in_with' => 'Inicia la sessió amb :socialDriver',
'sign_up_with' => 'Registra-mhi amb :socialDriver',
'sign_up' => 'Registreu-vos',
'log_in' => 'Inicia sessió',
'log_in_with' => 'Inicia sessió amb :socialDriver',
'sign_up_with' => 'Registreu-vos amb :socialDriver',
'logout' => 'Tanca la sessió',
'name' => 'Nom',
'username' => 'Nom dusuari',
'email' => 'Adreça electrònica',
'email' => 'Correu electrònic',
'password' => 'Contrasenya',
'password_confirm' => 'Confirmeu la contrasenya',
'password_hint' => 'Cal que tingui un mínim de 8 caràcters',
'password_hint' => 'Ha de tenir almenys 8 caràcters',
'forgot_password' => 'Heu oblidat la contrasenya?',
'remember_me' => 'Recordam',
'ldap_email_hint' => 'Introduïu una adreça electrònica per a aquest compte.',
'create_account' => 'Crea el compte',
'ldap_email_hint' => 'Introduïu un correu electrònic per al compte.',
'create_account' => 'Creeu un compte',
'already_have_account' => 'Ja teniu un compte?',
'dont_have_account' => 'No teniu cap compte?',
'social_login' => 'Inici de sessió amb xarxes socials',
'social_registration' => 'Registre amb xarxes socials',
'social_registration_text' => 'Registreu-vos i inicieu la sessió fent servir un altre servei.',
'social_login' => 'Inici de sessió amb una xarxa social',
'social_registration' => 'Registre amb una xarxa social',
'social_registration_text' => 'Registreu-vos i inicieu sessió amb un altre servei.',
'register_thanks' => 'Gràcies per registrar-vos!',
'register_confirm' => 'Reviseu el vostre correu electrònic i feu clic al botó de confirmació per a accedir a :appName.',
'registrations_disabled' => 'Actualment, els registres estan desactivats',
'registration_email_domain_invalid' => 'Aquest domini de correu electrònic no té accés a aquesta aplicació',
'register_success' => 'Gràcies per registrar-vos! Ja us hi heu registrat i heu iniciat la sessió.',
'register_thanks' => 'Gràcies per registrar-vos.',
'register_confirm' => 'Comproveu el correu electrònic i cliqueu el botó de confirmació per a accedir a :appName.',
'registrations_disabled' => 'Els registres estan desactivats',
'registration_email_domain_invalid' => 'Aquest domini de correu electrònic no té accés a laplicació.',
'register_success' => 'Gràcies per registrar-vos Us heu registrat i heu iniciat sessió.',
// Login auto-initiation
'auto_init_starting' => 'Sestà provant diniciar la sessió',
'auto_init_starting_desc' => 'Estem contactant amb el vostre sistema dautenticació per a començar el procés dinici de sessió. Si no hi ha cap progrés daquí a 5 segons, proveu de fer clic a lenllaç de sota.',
'auto_init_start_link' => 'Continua amb lautenticació',
'auto_init_starting' => 'Sestà provant diniciar sessió',
'auto_init_starting_desc' => 'Estem contactant amb el vostre sistema dautenticació per a començar el procés dinici sessió. Si daquí 5 segons no hi ha hagut cap progrés proveu de clicar lenllaç.',
'auto_init_start_link' => 'Continua amb la lautenticació',
// Password Reset
'reset_password' => 'Restableix la contrasenya',
'reset_password_send_instructions' => 'Introduïu la vostra adreça electrònica a continuació i us enviarem un correu electrònic amb un enllaç per a restablir la contrasenya.',
'reset_password_send_button' => 'Envia lenllaç de restabliment',
'reset_password_sent' => 'Senviarà un enllaç per a restablir la contrasenya a :email, si es troba aquesta adreça al sistema.',
'reset_password_success' => 'La contrasenya sha restablert correctament.',
'email_reset_subject' => 'Restabliu la contrasenya a :appName',
'email_reset_text' => 'Rebeu aquest correu electrònic perquè sha fet una petició de restabliment de contrasenya per al vostre compte.',
'email_reset_not_requested' => 'Si no heu demanat restablir la contrasenya, no cal que emprengueu cap acció.',
'reset_password' => 'Reinicialitza la contrasenya',
'reset_password_send_instructions' => 'Introduïu la vostra adreça electrònica i us enviarem un correu electrònic amb un enllaç de reinicialització de la contrasenya.',
'reset_password_send_button' => 'Enviam un enllaç de reinicialització',
'reset_password_sent' => 'Si ladreça electrònica :email existeix al sistema, us hi enviarem un enllaç de reinicialització de contrasenya.',
'reset_password_success' => 'Sha reinicialitzat la contrasenya.',
'email_reset_subject' => 'Reinicialització de la contrasenya de :appName',
'email_reset_text' => 'Heu rebut aquest correu electrònic perquè hem rebut una sol·licitud de reinicialització de contrasenya per al vostre compte.',
'email_reset_not_requested' => 'Si no heu sol·licitat la reinicialització de la contrasenya, no cal que feu res més.',
// Email Confirmation
'email_confirm_subject' => 'Confirmeu la vostra adreça electrònica a :appName',
'email_confirm_greeting' => 'Gràcies per unir-vos a :appName!',
'email_confirm_text' => 'Confirmeu la vostra adreça electrònica fent clic al botó a continuació:',
'email_confirm_action' => 'Confirma el correu',
'email_confirm_send_error' => 'Cal confirmar ladreça electrònica, però el sistema no ha pogut enviar el correu electrònic. Poseu-vos en contacte amb ladministrador perquè sasseguri que el correu electrònic està ben configurat.',
'email_confirm_success' => 'Heu confirmat el vostre correu electrònic! Ara hauríeu de poder iniciar la sessió fent servir aquesta adreça electrònica.',
'email_confirm_resent' => 'Sha tornat a enviar el correu electrònic de confirmació. Reviseu la vostra safata dentrada.',
'email_confirm_thanks' => 'Gràcies per la confirmació!',
'email_confirm_thanks_desc' => 'Espereu uns instants mentre gestionem la confirmació. Si no se us redirigeix daquí a 3 segons, premeu lenllaç «Continua» de sota per a continuar.',
'email_confirm_subject' => 'Confirmeu ladreça electrònica de :appName',
'email_confirm_greeting' => 'Gràcies per registrar-vos a :appName!',
'email_confirm_text' => 'Cliqueu el botó per a confirmar ladreça electrònica:',
'email_confirm_action' => 'Confirmeu dadreça electrònica',
'email_confirm_send_error' => 'Sha de confirmar de ladreça electrònica però no sha pogut enviar el correu de confirmació. Contacteu ladministrador per a assegurar-vos que el correu està ben configurat.',
'email_confirm_success' => 'Sha confirmat el correu electrònic. Ara hauríeu de poder iniciar sessió amb aquesta adreça electrònica.',
'email_confirm_resent' => 'Sha enviat el correu de confirmació. Comproveu la safata dentrada.',
'email_confirm_thanks' => 'Gràcies per la confirmació.',
'email_confirm_thanks_desc' => 'Espereu-vos un moment mentre es gestiona la confirmació. Si daquí 3 segons no se us ha redirigit, cliqueu lenllaç &laquo;Continua&raquo; per a continuar.',
'email_not_confirmed' => 'Adreça electrònica no confirmada',
'email_not_confirmed_text' => 'La vostra adreça electrònica encara no està confirmada.',
'email_not_confirmed_click_link' => 'Feu clic a lenllaç del correu electrònic que us vam enviar poc després que us registréssiu.',
'email_not_confirmed_resend' => 'Si no el trobeu, podeu tornar a enviar el correu electrònic de confirmació enviant el formulari a continuació.',
'email_not_confirmed' => 'No sha confirmat ladreça de correu electrònic',
'email_not_confirmed_text' => 'Encara no heu confirmat ladreça electrònica.',
'email_not_confirmed_click_link' => 'Cliqueu lenllaç que hi ha al correu electrònic que se us va enviar en registrar-vos.',
'email_not_confirmed_resend' => 'Si no trobeu el correu electrònic ompliu el formulari a continuació i us nenviarem un altre.',
'email_not_confirmed_resend_button' => 'Torna a enviar el correu de confirmació',
// User Invite
'user_invite_email_subject' => 'Us han convidat a unir-vos a :appName!',
'user_invite_email_greeting' => 'Sha creat un compte en el vostre nom a :appName.',
'user_invite_email_text' => 'Feu clic al botó a continuació per a definir una contrasenya per al compte i obtenir-hi accés:',
'user_invite_email_action' => 'Defineix una contrasenya per al compte',
'user_invite_email_subject' => 'Us han convidat a utilitzar :appName!',
'user_invite_email_greeting' => 'Us han creat un compte a :appName.',
'user_invite_email_text' => 'Cliqueu el botó per a configurar una contrasenya pel compte i poder-hi accedir:',
'user_invite_email_action' => 'Configura la contrasenya del compte',
'user_invite_page_welcome' => 'Us donem la benvinguda a :appName!',
'user_invite_page_text' => 'Per a enllestir el vostre compte i obtenir-hi accés, cal que definiu una contrasenya, que es farà servir per a iniciar la sessió a :appName en futures visites.',
'user_invite_page_text' => 'Per a poder accedir al compte heu de configurar una contrasenya que sutilitzarà per a iniciar sessió a :appName dara endavant.',
'user_invite_page_confirm_button' => 'Confirma la contrasenya',
'user_invite_success_login' => 'Sha definit la contrasenya. Ara hauríeu de poder iniciar la sessió fent servir la contrasenya que heu definit per a accedir a :appName!',
'user_invite_success_login' => 'Sha configurat la contrasenya. Ara hauríeu de poder iniciar sessió a :appName amb la contrasenya configurada.',
// Multi-factor Authentication
'mfa_setup' => 'Configura lautenticació de múltiple factor',
'mfa_setup_desc' => 'Configureu lautenticació de múltiple factor com a capa extra de seguretat en el vostre compte dusuari.',
'mfa_setup_configured' => 'Ja està configurada',
'mfa_setup' => 'Configuració de lautenticació multifactorial',
'mfa_setup_desc' => 'Configureu lautenticació multifactorial per a afegir una capa de seguretat extra al vostre compte dusuari.',
'mfa_setup_configured' => 'Ja està configurat',
'mfa_setup_reconfigure' => 'Torna-la a configurar',
'mfa_setup_remove_confirmation' => 'Segur que voleu suprimir aquest mètode dautenticació de múltiple factor?',
'mfa_setup_remove_confirmation' => 'Esteu segur que voleu eliminar aquest mètode dautenticació multifactorial?',
'mfa_setup_action' => 'Configura',
'mfa_backup_codes_usage_limit_warning' => 'Teniu menys de 5 codis de seguretat restants. Genereu-ne i emmagatzemeu-ne un nou conjunt abans que se us acabin perquè no perdeu laccés al vostre compte.',
'mfa_backup_codes_usage_limit_warning' => 'Us queden menys de 5 codis de suport. Genereu-ne de nous i deseu-los abans de quedar-vos-en sense per a evitar perdre laccés al compte.',
'mfa_option_totp_title' => 'Aplicació mòbil',
'mfa_option_totp_desc' => 'Per a fer servir lautenticació de múltiple factor us caldrà una aplicació mòbil que suporti TOTP, com ara Google Authenticador, Authy o Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Codis de seguretat',
'mfa_option_backup_codes_desc' => 'Deseu de manera segura codis de seguretat dun sol ús que podeu introduir per a verificar la vostra identitat.',
'mfa_option_totp_desc' => 'Per a utilitzar lautenticació multifactorial heu de tenir una aplicació que sigui compatible amb TOPT (contrasenyes dun sol ús basades en el temps) com ara Google Authenticator, Authy, o Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Codis de suport',
'mfa_option_backup_codes_desc' => 'Genereu un joc de codis de suport dun sol ús que haureu dintroduir per a verificar la vostra identitat. Assegureu-vos de desar-los en un lloc segur.',
'mfa_gen_confirm_and_enable' => 'Confirma i activa',
'mfa_gen_backup_codes_title' => 'Configuració de codis de seguretat',
'mfa_gen_backup_codes_desc' => 'Deseu la següent llista de codis en un lloc segur. Quan accediu al sistema, podeu fer servir un dels codis com a segon mètode dautenticació.',
'mfa_gen_backup_codes_title' => 'Configuració dels codis de suport',
'mfa_gen_backup_codes_desc' => 'Deseu els codis en un lloc segur. Podreu utilitzar un dels codis com a un altre mètode dautenticació per a iniciar sessió.',
'mfa_gen_backup_codes_download' => 'Baixa els codis',
'mfa_gen_backup_codes_usage_warning' => 'Cada codi es pot fer servir només una vegada',
'mfa_gen_backup_codes_usage_warning' => 'Només podeu utilitzar cada codi un sol cop.',
'mfa_gen_totp_title' => 'Configuració de laplicació mòbil',
'mfa_gen_totp_desc' => 'Per a fer servir lautenticació de múltiple factor us caldrà una aplicació mòbil que suporti TOTP, com ara Google Authenticador, Authy o Microsoft Authenticator.',
'mfa_gen_totp_scan' => 'Per a començar, escanegeu el codi QR següent fent servir la vostre aplicació dautenticació preferida.',
'mfa_gen_totp_verify_setup' => 'Verifica la configuració',
'mfa_gen_totp_verify_setup_desc' => 'Verifiqueu que tot funciona introduint un codi, generat amb laplicació dautenticació, a la capsa de text següent:',
'mfa_gen_totp_provide_code_here' => 'Proporcioneu aquí el codi generat per laplicació',
'mfa_gen_totp_desc' => 'Per a utilitzar lautenticació multifactorial heu de tenir una aplicació que sigui compatible amb TOPT (contrasenyes dun sol ús basades en el temps) com ara Google Authenticator, Authy, o Microsoft Authenticator.',
'mfa_gen_totp_scan' => 'Escanegeu el codi QR amb laplicació dautenticació que preferiu per a començar.',
'mfa_gen_totp_verify_setup' => 'Verificació de la configuració',
'mfa_gen_totp_verify_setup_desc' => 'Introduïu un dels codis generats per laplicació dautenticació per a verificar que tot funciona:',
'mfa_gen_totp_provide_code_here' => 'Codi',
'mfa_verify_access' => 'Verifica laccés',
'mfa_verify_access_desc' => 'El vostre compte dusuari requereix que confirmeu la vostra identitat amb un nivell addicional de verificació abans que pugueu obtenir-hi accés. Verifiqueu-la fent servir un dels vostres mètodes configurats per a continuar.',
'mfa_verify_access_desc' => 'Heu de verificar la vostra identitat amb un nivell de verificació addicional per a iniciar sessió. Verifiqueu-la amb un dels mètodes que heu configurat per a continuar.',
'mfa_verify_no_methods' => 'No hi ha cap mètode configurat',
'mfa_verify_no_methods_desc' => 'No sha trobat cap mètode dautenticació de múltiple factor al vostre compte. Cal que configureu almenys un mètode abans dobtenir-hi accés.',
'mfa_verify_use_totp' => 'Verifica fent servir una aplicació mòbil',
'mfa_verify_use_backup_codes' => 'Verifica fent servir un codi de seguretat',
'mfa_verify_backup_code' => 'Codi de seguretat',
'mfa_verify_backup_code_desc' => 'Introduïu un dels vostres codis de seguretat restants a continuació:',
'mfa_verify_backup_code_enter_here' => 'Introduïu aquí el codi de seguretat',
'mfa_verify_totp_desc' => 'Introduïu el codi generat amb la vostra aplicació mòbil a continuació:',
'mfa_setup_login_notification' => 'Sha configurat el mètode dautenticació de múltiple factor. Torneu a iniciar la sessió fent servir el mètode que heu configurat.',
'mfa_verify_no_methods_desc' => 'No hi ha cap mètode dautenticació multifactorial configurat al vostre compte. Heu de configurar com a mínim un mètode per a iniciar sessió.',
'mfa_verify_use_totp' => 'Verificació amb una aplicació mòbil',
'mfa_verify_use_backup_codes' => 'Verificació amb un codi de suport',
'mfa_verify_backup_code' => 'Codi de suport',
'mfa_verify_backup_code_desc' => 'Introduïu un dels codis de suport que us quedin:',
'mfa_verify_backup_code_enter_here' => 'Codi:',
'mfa_verify_totp_desc' => 'Introduïu el codi generat per laplicació mòbil:',
'mfa_setup_login_notification' => 'Sha configurat el mètode multifactorial. Torneu a iniciar sessió amb el mètode configurat.',
];

View File

@@ -7,8 +7,8 @@ return [
// Buttons
'cancel' => 'Cancel·la',
'close' => 'Tanca',
'confirm' => 'Dacord',
'back' => 'Enrere',
'confirm' => 'Confirma',
'back' => 'Torna',
'save' => 'Desa',
'continue' => 'Continua',
'select' => 'Selecciona',
@@ -18,14 +18,14 @@ return [
// Form Labels
'name' => 'Nom',
'description' => 'Descripció',
'role' => 'Rol',
'cover_image' => 'Imatge de portada',
'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.',
'role' => 'Funció',
'cover_image' => 'Imatge de coberta',
'cover_image_description' => 'La imatge ha de ser duns 440×250px, però quan calgui se najustarà la mida o sescapçarà per a adaptar-la a la interfície dusuari i les mides amb què es mostrarà canviaran.',
// Actions
'actions' => 'Accions',
'view' => 'Visualitza',
'view_all' => 'Visualitza-ho tot',
'view' => 'Mostra',
'view_all' => 'Mostra-ho tot',
'new' => 'Nou',
'create' => 'Crea',
'update' => 'Actualitza',
@@ -44,10 +44,10 @@ return [
'configure' => 'Configura',
'manage' => 'Gestiona',
'fullscreen' => 'Pantalla completa',
'favourite' => 'Afegeix a preferits',
'unfavourite' => 'Suprimeix de preferits',
'next' => 'Següent',
'previous' => 'Anterior',
'favourite' => 'Afegeix als favorits',
'unfavourite' => 'Suprimeix dels favorits',
'next' => 'Endavant',
'previous' => 'Endarrere',
'filter_active' => 'Filtre actiu:',
'filter_clear' => 'Esborra el filtre',
'download' => 'Baixa',
@@ -56,26 +56,26 @@ return [
// Sort Options
'sort_options' => 'Opcions dordenació',
'sort_direction_toggle' => 'Commuta la direcció de lordenació',
'sort_ascending' => 'Ordre ascendent',
'sort_descending' => 'Ordre descendent',
'sort_direction_toggle' => 'Inverteix lordre',
'sort_ascending' => 'Ordenació ascendent',
'sort_descending' => 'Ordenació descendent',
'sort_name' => 'Nom',
'sort_default' => 'Per defecte',
'sort_default' => 'Predeterminat',
'sort_created_at' => 'Data de creació',
'sort_updated_at' => 'Data dactualització',
// Misc
'deleted_user' => 'Usuari suprimit',
'no_activity' => 'No hi ha activitat',
'no_items' => 'No hi ha cap element',
'back_to_top' => 'Torna a dalt',
'skip_to_main_content' => 'Vés al contingut',
'no_activity' => 'No hi ha cap activitat per mostrar',
'no_items' => 'No hi ha cap element disponible',
'back_to_top' => 'Amunt',
'skip_to_main_content' => 'Ves al contingut principal',
'toggle_details' => 'Commuta els detalls',
'toggle_thumbnails' => 'Commuta les miniatures',
'details' => 'Detalls',
'grid_view' => 'Visualització en graella',
'list_view' => 'Visualització en llista',
'default' => 'Per defecte',
'default' => 'Predeterminat',
'breadcrumb' => 'Ruta de navegació',
'status' => 'Estat',
'status_active' => 'Actiu',
@@ -84,8 +84,8 @@ return [
'none' => 'Cap',
// Header
'homepage' => 'Pàgina principal',
'header_menu_expand' => 'Expandeix el menú de la capçalera',
'homepage' => 'Pàgina dinici',
'header_menu_expand' => 'Desplega el menú de la capçalera',
'profile_menu' => 'Menú del perfil',
'view_profile' => 'Mostra el perfil',
'edit_profile' => 'Edita el perfil',
@@ -100,11 +100,14 @@ return [
'tab_content_label' => 'Pestanya: Mostra el contingut principal',
// Email Content
'email_action_help' => 'Si teniu problemes per a fer clic al botó «:actionText», copieu i enganxeu lURL següent al vostre navegador web:',
'email_action_help' => 'Si el botó &laquo;:actionText&raquo; no funciona, copieu i enganxeu lURL següent al vostre navegador:',
'email_rights' => 'Tots els drets reservats',
// Footer Link Options
// Not directly used but available for convenience to users.
'privacy_policy' => 'Política de privadesa',
'terms_of_service' => 'Condicions del servei',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@@ -9,37 +9,37 @@ return [
'image_list' => 'Llista dimatges',
'image_details' => 'Detalls de la imatge',
'image_upload' => 'Puja una imatge',
'image_intro' => 'Aquí podeu seleccionar i gestionar imatges que shagin pujat anteriorment al sistema.',
'image_intro_upload' => 'Pugeu una imatge nova arrossegant un fitxer dimatge a aquesta finestra o fent servir el botó de «Puja una imatge».',
'image_intro' => 'Seleccioneu i gestioneu les imatges que shan pujat al sistema amb anterioritat.',
'image_intro_upload' => 'Pugeu una imatge arrossegant-la i deixant-la anar en aquesta finestra o amb el botó &laquo;Puja una imatge&raquo;.',
'image_all' => 'Totes',
'image_all_title' => 'Mostra totes les imatges',
'image_book_title' => 'Mostra les imatges pujades a aquest llibre',
'image_page_title' => 'Mostra les imatges pujades a aquesta pàgina',
'image_search_hint' => 'Cerca per nom dimatge',
'image_uploaded' => 'Pujada :uploadedDate',
'image_book_title' => 'Mostra les imatges pujades en aquest llibre',
'image_page_title' => 'Mostra les imatges pujades en aquesta pàgina',
'image_search_hint' => 'Cerca pel nom de la imatge',
'image_uploaded' => 'Pujada el :uploadedDate',
'image_uploaded_by' => 'Pujada per :userName',
'image_uploaded_to' => 'Pujada a :pageLink',
'image_updated' => 'Actualitzada :updateDate',
'image_updated' => 'Actualitzada el :updateDate',
'image_load_more' => 'Carregan més',
'image_image_name' => 'Nom de la imatge',
'image_delete_used' => 'Aquesta imatge sutilitza a les pàgines següents.',
'image_delete_confirm_text' => 'Segur que voleu suprimir aquesta imatge?',
'image_select_image' => 'Selecciona una imatge',
'image_dropzone' => 'Arrossegueu imatges o feu clic aquí per a pujar-les',
'image_dropzone_drop' => 'Arrossegueu imatges aquí per a pujar-les',
'image_delete_used' => 'Aquesta imatge sutilitza en les pàgines següents.',
'image_delete_confirm_text' => 'Esteu segur que voleu suprimir aquesta imatge?',
'image_select_image' => 'Selecció dimatges',
'image_dropzone' => 'Deixeu-hi anar les imatges o cliqueu aquí per a pujar-ne',
'image_dropzone_drop' => 'Deixeu-hi anar les imatges per a pujar-les',
'images_deleted' => 'Imatges suprimides',
'image_preview' => 'Previsualització de la imatge',
'image_upload_success' => 'Imatge pujada correctament',
'image_update_success' => 'Detalls de la imatge actualitzats correctament',
'image_delete_success' => 'Imatge suprimida correctament',
'image_upload_success' => 'Sha pujat la imatge',
'image_update_success' => 'Shan actualitzat els detalls de la imatge',
'image_delete_success' => 'Sha suprimit la imatge',
'image_replace' => 'Substitueix la imatge',
'image_replace_success' => 'Fitxer de la imatge actualitzat correctament',
'image_replace_success' => 'Sha substituït la imatge',
'image_rebuild_thumbs' => 'Regenera les variacions de mida',
'image_rebuild_thumbs_success' => 'Variacions de mida de la imatge regenerades correctament!',
'image_rebuild_thumbs_success' => 'Shan regenerat les variacions de mida',
// Code Editor
'code_editor' => 'Edita el codi',
'code_language' => 'Llenguatge del codi',
'code_language' => 'Llenguatge de codificació',
'code_content' => 'Contingut del codi',
'code_session_history' => 'Historial de la sessió',
'code_save' => 'Desa el codi',

View File

@@ -18,27 +18,27 @@ return [
'left' => 'Esquerra',
'center' => 'Centre',
'right' => 'Dreta',
'top' => 'Superior',
'middle' => 'Centre',
'bottom' => 'Inferior',
'top' => 'Dalt',
'middle' => 'Mig',
'bottom' => 'Baix',
'width' => 'Amplada',
'height' => 'Alçada',
'height' => 'Altura',
'More' => 'Més',
'select' => 'Selecciona...',
'select' => 'Selecciona',
// Toolbar
'formats' => 'Formats',
'header_large' => 'Capçalera grossa',
'header_medium' => 'Capçalera mitjana',
'header_small' => 'Capçalera petita',
'header_tiny' => 'Capçalera minúscula',
'header_large' => 'Títol gros',
'header_medium' => 'Títol mitjà',
'header_small' => 'Títol petit',
'header_tiny' => 'Títol minúscul',
'paragraph' => 'Paràgraf',
'blockquote' => 'Citació',
'inline_code' => 'Codi en línia',
'callouts' => 'Llegendes',
'blockquote' => 'Bloc de cita',
'inline_code' => 'Línia de codi',
'callouts' => 'Crides',
'callout_information' => 'Informació',
'callout_success' => 'Èxit',
'callout_warning' => 'Advertència',
'callout_warning' => 'Advertiment',
'callout_danger' => 'Perill',
'bold' => 'Negreta',
'italic' => 'Cursiva',
@@ -48,30 +48,30 @@ return [
'subscript' => 'Subíndex',
'text_color' => 'Color del text',
'custom_color' => 'Color personalitzat',
'remove_color' => 'Suprimeix el color',
'remove_color' => 'Elimina el color',
'background_color' => 'Color de fons',
'align_left' => 'Alinea a lesquerra',
'align_center' => 'Alinea al centre',
'align_right' => 'Alinea a la dreta',
'align_justify' => 'Justifica',
'list_bullet' => 'Llista amb pics',
'list_bullet' => 'Llista de pics',
'list_numbered' => 'Llista numerada',
'list_task' => 'Llista de tasques',
'indent_increase' => 'Augmenta el sagnat',
'indent_decrease' => 'Redueix el sagnat',
'list_task' => 'Llista de caselles',
'indent_increase' => 'Augmenta la sagnia',
'indent_decrease' => 'Redueix la sagnia',
'table' => 'Taula',
'insert_image' => 'Insereix una imatge',
'insert_image_title' => 'Insereix/edita una imatge',
'insert_link' => 'Insereix/edita un enllaç',
'insert_link_title' => 'Insereix/edita un enllaç',
'insert_image_title' => 'Insereix o edita una imatge',
'insert_link' => 'Insereix un enllaç',
'insert_link_title' => 'Insereix o edita un enllaç',
'insert_horizontal_line' => 'Insereix una línia horitzontal',
'insert_code_block' => 'Insereix un bloc de codi',
'edit_code_block' => 'Edita un bloc de codi',
'insert_drawing' => 'Insereix/edita un dibuix',
'drawing_manager' => 'Gestor de dibuixos',
'insert_media' => 'Insereix/edita un mitjà',
'insert_media_title' => 'Insereix/edita un mitjà',
'clear_formatting' => 'Neteja el format',
'edit_code_block' => 'Edita el bloc de codi',
'insert_drawing' => 'Insereix o edita un diagrama',
'drawing_manager' => 'Gestor de diagrames',
'insert_media' => 'Insereix o edita un contingut multimèdia',
'insert_media_title' => 'Insereix o edita un contingut multimèdia',
'clear_formatting' => 'Lleva el format',
'source_code' => 'Codi font',
'source_code_title' => 'Codi font',
'fullscreen' => 'Pantalla completa',
@@ -81,9 +81,9 @@ return [
'table_properties' => 'Propietats de la taula',
'table_properties_title' => 'Propietats de la taula',
'delete_table' => 'Suprimeix la taula',
'table_clear_formatting' => 'Clear table formatting',
'resize_to_contents' => 'Resize to contents',
'row_header' => 'Row header',
'table_clear_formatting' => 'Lleva el format de la taula',
'resize_to_contents' => 'Ajusta al contingut',
'row_header' => 'Capçalera de la fila',
'insert_row_before' => 'Insereix una fila abans',
'insert_row_after' => 'Insereix una fila després',
'delete_row' => 'Suprimeix la fila',
@@ -97,10 +97,10 @@ return [
'cell_properties_title' => 'Propietats de la cel·la',
'cell_type' => 'Tipus de cel·la',
'cell_type_cell' => 'Cel·la',
'cell_scope' => 'Àmbit',
'cell_scope' => 'Àmbit de la cel·la',
'cell_type_header' => 'Cel·la de capçalera',
'merge_cells' => 'Fusiona les cel·les',
'split_cell' => 'Divideix les cel·les',
'merge_cells' => 'Combina les cel·les',
'split_cell' => 'Divideix la cel·la',
'table_row_group' => 'Grup de files',
'table_column_group' => 'Grup de columnes',
'horizontal_align' => 'Alineació horitzontal',
@@ -117,61 +117,61 @@ return [
'row_type' => 'Tipus de fila',
'row_type_header' => 'Capçalera',
'row_type_body' => 'Cos',
'row_type_footer' => 'Peu de pàgina',
'row_type_footer' => 'Peu',
'alignment' => 'Alineació',
'cut_column' => 'Retalla la columna',
'copy_column' => 'Copia la columna',
'paste_column_before' => 'Enganxa la columna abans',
'paste_column_after' => 'Enganxa la columna després',
'cell_padding' => 'Separació de la cel·la',
'cell_spacing' => 'Espaiat de la cel·la',
'caption' => 'Títol',
'show_caption' => 'Mostra el títol',
'cell_padding' => 'Separació de cel·la',
'cell_spacing' => 'Espaiat de cel·la',
'caption' => 'Llegenda',
'show_caption' => 'Mostra la llegenda',
'constrain' => 'Mantén les proporcions',
'cell_border_solid' => 'Sòlida',
'cell_border_dotted' => 'Puntejada',
'cell_border_dashed' => 'Discontínua',
'cell_border_dotted' => 'De punts',
'cell_border_dashed' => 'De guions',
'cell_border_double' => 'Doble',
'cell_border_groove' => 'Bisellada',
'cell_border_ridge' => 'Emmarcada',
'cell_border_inset' => 'Enfonsada',
'cell_border_outset' => 'Ressaltada',
'cell_border_none' => 'Cap',
'cell_border_hidden' => 'Oculta',
'cell_border_groove' => 'Vora enfonsada',
'cell_border_ridge' => 'Vora ressaltada',
'cell_border_inset' => 'Fons enfonsat',
'cell_border_outset' => 'Fons ressaltat',
'cell_border_none' => 'Sense vora',
'cell_border_hidden' => 'Amagada',
// Images, links, details/summary & embed
'source' => 'Origen',
'source' => 'Font',
'alt_desc' => 'Descripció alternativa',
'embed' => 'Incrustació',
'paste_embed' => 'Enganxeu el codi dincrustació a continuació:',
'embed' => 'Incrusta',
'paste_embed' => 'Enganxa el codi per a incrustar a sota:',
'url' => 'URL',
'text_to_display' => 'Text a mostrar',
'text_to_display' => 'Text per a mostrar',
'title' => 'Títol',
'open_link' => 'Obre lenllaç',
'open_link_in' => 'Obre lenllaç a...',
'open_link_current' => 'Finestra actual',
'open_link_new' => 'Finestra nova',
'remove_link' => 'Suprimeix lenllaç',
'insert_collapsible' => 'Insereix un bloc contraïble',
'collapsible_unwrap' => 'Deixa de contraure',
'open_link_in' => 'Obre lenllaç',
'open_link_current' => 'A la finestra actual',
'open_link_new' => 'En una finestra nova',
'remove_link' => 'Lleva lenllaç',
'insert_collapsible' => 'Insereix un bloc desplegable',
'collapsible_unwrap' => 'Lleva el bloc desplegable',
'edit_label' => 'Edita letiqueta',
'toggle_open_closed' => 'Obre/tanca',
'collapsible_edit' => 'Edita el bloc contraïble',
'toggle_open_closed' => 'Obre o tanca',
'collapsible_edit' => 'Edita el bloc desplegable',
'toggle_label' => 'Commuta letiqueta',
// About view
'about' => 'Quant a leditor',
'about_title' => 'Quant a leditor WYSIWYG',
'editor_license' => 'Llicència i copyright de leditor',
'editor_tiny_license' => 'Aquest editor sha construït amb :tinyLink, que es proporciona sota la llicència MIT.',
'editor_tiny_license_link' => 'Podeu trobar els detalls de la llicència i el copyright del TinyMCE aquí.',
'about' => 'Quant a lEditor',
'about_title' => 'Quant a lEditor WYSIWYG',
'editor_license' => 'Llicència i copyright de lEditor',
'editor_tiny_license' => 'Aquest editor sha creat amb :tinyLink que es proporciona amb la llicència MIT.',
'editor_tiny_license_link' => 'Detalls de la llicència i el copyright de TinyMCE.',
'save_continue' => 'Desa la pàgina i continua',
'callouts_cycle' => '(Continueu prement per a commutar els diferents tipus)',
'link_selector' => 'Enllaç al contingut',
'shortcuts' => 'Dreceres',
'callouts_cycle' => '(Continueu prement per a canviar entre tipus)',
'link_selector' => 'Enllaç a contingut',
'shortcuts' => 'Dreceres de teclat',
'shortcut' => 'Drecera',
'shortcuts_intro' => 'Hi ha les següents dreceres disponibles a leditor:',
'windows_linux' => '(Windows/Linux)',
'shortcuts_intro' => 'A leditor, hi ha disponibles les dreceres de teclat següents:',
'windows_linux' => '(Windows o Linux)',
'mac' => '(Mac)',
'description' => 'Descripció',
];

View File

@@ -6,34 +6,34 @@
return [
// Shared
'recently_created' => 'Creat fa poc',
'recently_created_pages' => 'Pàgines creades fa poc',
'recently_updated_pages' => 'Pàgines actualitzades fa poc',
'recently_created_chapters' => 'Capítols creats fa poc',
'recently_created_books' => 'Llibres creats fa poc',
'recently_created_shelves' => 'Prestatges creats fa poc',
'recently_update' => 'Actualitzat fa poc',
'recently_viewed' => 'Vist fa poc',
'recently_created' => 'Elements creats recentment',
'recently_created_pages' => 'Pàgines creades recentment',
'recently_updated_pages' => 'Pàgines actualitzades recentment',
'recently_created_chapters' => 'Capítols creats recentment',
'recently_created_books' => 'Llibres creats recentment',
'recently_created_shelves' => 'Prestatges creats recentment',
'recently_update' => 'Elements actualitzats recentment',
'recently_viewed' => 'Elements vistos recentment',
'recent_activity' => 'Activitat recent',
'create_now' => 'Crean ara',
'create_now' => 'Crea un element',
'revisions' => 'Revisions',
'meta_revision' => 'Revisió núm. :revisionCount',
'meta_created' => 'Creat :timeLength',
'meta_created_name' => 'Creat :timeLength per :user',
'meta_updated' => 'Actualitzat :timeLength',
'meta_updated_name' => 'Actualitzat :timeLength per :user',
'meta_created' => 'Sha creat :timeLength',
'meta_created_name' => ':user lha creat :timeLength',
'meta_updated' => 'Sha actualitzat :timeLength',
'meta_updated_name' => ':user lha actualitzat :timeLength',
'meta_owned_name' => 'Propietat de :user',
'meta_reference_count' => 'Hi fa referència :count element|Hi fan referència :count elements',
'entity_select' => 'Selecciona una entitat',
'meta_reference_count' => 'Shi fa referència en :count element|Shi fa referència en :count elements',
'entity_select' => 'Selecció de lentitat',
'entity_select_lack_permission' => 'No teniu els permisos necessaris per a seleccionar aquest element',
'images' => 'Imatges',
'my_recent_drafts' => 'Els vostres esborranys recents',
'my_recently_viewed' => 'Les vostres visualitzacions recents',
'my_most_viewed_favourites' => 'Els vostres preferits més vistos',
'my_favourites' => 'Els vostres preferits',
'my_recent_drafts' => 'Els meus esborranys recents',
'my_recently_viewed' => 'Els meus vistos recentment',
'my_most_viewed_favourites' => 'Els meus favorits més vistos',
'my_favourites' => 'Els meus favorits',
'no_pages_viewed' => 'No heu vist cap pàgina',
'no_pages_recently_created' => 'No sha creat cap pàgina fa poc',
'no_pages_recently_updated' => 'No sha actualitzat cap pàgina fa poc',
'no_pages_recently_created' => 'No sha creat cap pàgina recentment',
'no_pages_recently_updated' => 'No sha actualitzat cap pàgina recentment',
'export' => 'Exporta',
'export_html' => 'Fitxer web independent',
'export_pdf' => 'Fitxer PDF',
@@ -45,40 +45,40 @@ return [
// Permissions and restrictions
'permissions' => 'Permisos',
'permissions_desc' => 'Definiu aquí permisos que tindran preferència sobre les permisos per defecte proporcionats pels rols dusuari.',
'permissions_book_cascade' => 'Els permisos definits en llibres shereten automàticament en pàgines i capítols inferiors, llevat que tinguin permisos propis definits.',
'permissions_chapter_cascade' => 'Els permisos definits en capítols shereten automàticament en pàgines inferiors, llevat que tinguin permisos propis definits.',
'permissions_desc' => 'Configureu aquí els permisos per a sobreescriure els permisos per defecte proporcionats pels rols dusuari.',
'permissions_book_cascade' => 'Els permisos dels llibres saplicaran també als capítols i les pàgines que continguin llevat que tinguin els seus propis permisos.',
'permissions_chapter_cascade' => 'Els permisos dels capítols saplicaran també a les pàgines que continguin llevat que tinguin els seus propis permisos.',
'permissions_save' => 'Desa els permisos',
'permissions_owner' => 'Propietari',
'permissions_role_everyone_else' => 'Tota la resta',
'permissions_role_everyone_else_desc' => 'Definiu permisos per a tots els rols no definits específicament.',
'permissions_role_override' => 'Força els permisos per al rol',
'permissions_inherit_defaults' => 'Hereta els permisos per defecte',
'permissions_role_everyone_else' => 'La resta dels usuaris',
'permissions_role_everyone_else_desc' => 'Configureu permisos per a tots els rols que no shagin sobreescrit específicament.',
'permissions_role_override' => 'Sobreescriu els permisos per al rol',
'permissions_inherit_defaults' => 'Hereta la configuració per defecte',
// Search
'search_results' => 'Resultats de la cerca',
'search_total_results_found' => 'Sha trobat :count resultat|Shan trobat :count resultats en total',
'search_total_results_found' => 'Sha trobat :count coincidència|Shan trobat :count coincidències',
'search_clear' => 'Esborra la cerca',
'search_no_pages' => 'La cerca no coincideix amb cap pàgina',
'search_no_pages' => 'No sha trobat cap pàgina que coincideixi amb la cerca',
'search_for_term' => 'Cerca :term',
'search_more' => 'Més resultats',
'search_advanced' => 'Cerca avançada',
'search_terms' => 'Termes de la cerca',
'search_terms' => 'Termes de cerca',
'search_content_type' => 'Tipus de contingut',
'search_exact_matches' => 'Coincidències exactes',
'search_tags' => 'Cerca detiquetes',
'search_options' => 'Opcions',
'search_viewed_by_me' => 'Visualitzat per mi',
'search_not_viewed_by_me' => 'No visualitzat per mi',
'search_permissions_set' => 'Amb permisos definits',
'search_created_by_me' => 'Creat per mi',
'search_updated_by_me' => 'Actualitzat per mi',
'search_owned_by_me' => 'En sóc propietari',
'search_date_options' => 'Opcions de dates',
'search_updated_before' => 'Actualitzat abans de',
'search_updated_after' => 'Actualitzat després de',
'search_created_before' => 'Creat abans de',
'search_created_after' => 'Creat després de',
'search_viewed_by_me' => 'Vistos per mi',
'search_not_viewed_by_me' => 'No vistos per mi',
'search_permissions_set' => 'Amb permisos',
'search_created_by_me' => 'Creats per mi',
'search_updated_by_me' => 'Actualitzats per mi',
'search_owned_by_me' => 'En soc propietari',
'search_date_options' => 'Opcions de data',
'search_updated_before' => 'Actualitzats abans de',
'search_updated_after' => 'Actualitzats després de',
'search_created_before' => 'Creats abans de',
'search_created_after' => 'Creats després de',
'search_set_date' => 'Defineix una data',
'search_update' => 'Actualitza la cerca',
@@ -86,87 +86,87 @@ return [
'shelf' => 'Prestatge',
'shelves' => 'Prestatges',
'x_shelves' => ':count prestatge|:count prestatges',
'shelves_empty' => 'No hi ha cap prestatge creat',
'shelves_create' => 'Crea un prestatge nou',
'shelves_empty' => 'No hi ha cap prestatge',
'shelves_create' => 'Crea un prestatge',
'shelves_popular' => 'Prestatges populars',
'shelves_new' => 'Prestatges nous',
'shelves_new_action' => 'Prestatge nou',
'shelves_popular_empty' => 'Aquí apareixeran els prestatges més populars.',
'shelves_new_empty' => 'Aquí apareixeran els prestatges creats fa poc.',
'shelves_new_empty' => 'Aquí apareixeran els prestatges més recents.',
'shelves_save' => 'Desa el prestatge',
'shelves_books' => 'Llibres en aquest prestatge',
'shelves_add_books' => 'Afegeix llibres a aquest prestatge',
'shelves_drag_books' => 'Arrossegueu llibres a sota per a afegir-los a aquest prestatge',
'shelves_empty_contents' => 'Aquest prestatge no té cap llibre assignat',
'shelves_edit_and_assign' => 'Editeu el prestatge per a assignar-hi llibres',
'shelves_edit_named' => 'Edita el prestatge :name',
'shelves_add_books' => 'Afegiu llibres en aquest prestatge',
'shelves_drag_books' => 'Arrossegueu i deixeu anar llibres a sota per a afegir-los en aquest prestatge',
'shelves_empty_contents' => 'Aquest prestatge no té cap llibre',
'shelves_edit_and_assign' => 'Editeu el prestatge per a afegir-hi llibres',
'shelves_edit_named' => 'Edita el prestatge &laquo;:name&raquo;',
'shelves_edit' => 'Edita el prestatge',
'shelves_delete' => 'Suprimeix el prestatge',
'shelves_delete_named' => 'Suprimeix el prestatge :name',
'shelves_delete_explain' => "Se suprimirà el prestatge amb el nom «:name». Els llibres que contingui no se suprimiran.",
'shelves_delete_confirmation' => 'Segur que voleu suprimir aquest prestatge?',
'shelves_delete_named' => 'Suprimeix del prestatge &laquo;:name&raquo;',
'shelves_delete_explain' => "Se suprimirà el prestatge &laquo;:name&raquo;. No se suprimiran els llibres que contingui.",
'shelves_delete_confirmation' => 'Esteu segur que voleu suprimir aquest prestatge?',
'shelves_permissions' => 'Permisos del prestatge',
'shelves_permissions_updated' => 'Shan actualitzat els permisos del prestatge',
'shelves_permissions_active' => 'Permisos del prestatge actius',
'shelves_permissions_cascade_warning' => 'Els permisos dels prestatges no shereten automàticament en els llibres que contenen. És així perquè un llibre pot estar en múltiples prestatges. Es poden copiar, però, els permisos als llibres continguts fent servir lopció a continuació.',
'shelves_permissions_create' => 'Els permisos de crear prestatges només es fan servir per a copiar permisos a llibres continguts fent servir lacció següent. No controlen la capacitat de crear llibres.',
'shelves_permissions_active' => 'El prestatge té permisos',
'shelves_permissions_cascade_warning' => 'Els permisos dels prestatges no saplicaran als llibres que contingui. Això és perquè els llibres poden estar en més dun prestatge a la vegada. Això no obstant, els permisos es poden copiar als llibres que contingui amb lopció de sota.',
'shelves_permissions_create' => 'Els permisos per a crear prestatges només sutilitzen per a copiar permisos als llibres que contenen amb lacció de sota. No controlen els permisos per a crear llibres.',
'shelves_copy_permissions_to_books' => 'Copia els permisos als llibres',
'shelves_copy_permissions' => 'Copia els permisos',
'shelves_copy_permissions_explain' => 'Saplicarà la configuració de permisos actual a tots els llibres continguts. Abans dactivar-la, assegureu-vos que hàgiu desat qualsevol canvi en els permisos daquest prestatge.',
'shelves_copy_permissions_explain' => 'Saplicaran els permisos del prestatge a tots els llibres que contingui. Abans de continuar assegureu-vos que heu desat els canvis que hàgiu fet als permisos del prestatge.',
'shelves_copy_permission_success' => 'Shan copiat els permisos del prestatge a :count llibres',
// Books
'book' => 'Llibre',
'books' => 'Llibres',
'x_books' => ':count llibre|:count llibres',
'books_empty' => 'No hi ha cap llibre creat',
'books_empty' => 'No hi ha cap llibre',
'books_popular' => 'Llibres populars',
'books_recent' => 'Llibres recents',
'books_new' => 'Llibres nous',
'books_new_action' => 'Llibre nou',
'books_popular_empty' => 'Aquí apareixeran els llibres més populars.',
'books_new_empty' => 'Aquí apareixeran els llibres creats fa poc.',
'books_create' => 'Crea un llibre nou',
'books_new_empty' => 'Aquí apareixeran els llibres més recents.',
'books_create' => 'Crea un llibre',
'books_delete' => 'Suprimeix el llibre',
'books_delete_named' => 'Suprimeix el llibre :bookName',
'books_delete_explain' => 'Se suprimirà el llibre amb el nom «:bookName». Sen suprimiran totes les pàgines i tots els capítols.',
'books_delete_confirmation' => 'Segur que voleu suprimir aquest llibre?',
'books_delete_named' => 'Suprimeix el llibre &laquo;:bookName&raquo;',
'books_delete_explain' => 'Se suprimirà el llibre &laquo;:bookName&raquo;. Se suprimiran totes les pàgines i capítols.',
'books_delete_confirmation' => 'Esteu segur que voleu suprimir aquest llibre?',
'books_edit' => 'Edita el llibre',
'books_edit_named' => 'Edita el llibre :bookName',
'books_form_book_name' => 'Nom del llibre',
'books_edit_named' => 'Edita el llibre &laquo;:bookName&raquo;',
'books_form_book_name' => 'Títol del llibre',
'books_save' => 'Desa el llibre',
'books_permissions' => 'Permisos del llibre',
'books_permissions_updated' => 'Shan actualitzat els permisos del llibre',
'books_empty_contents' => 'No hi ha cap pàgina ni cap capítol creat en aquest llibre.',
'books_empty_create_page' => 'Crea una pàgina nova',
'books_empty_sort_current_book' => 'Ordena el llibre actual',
'books_permissions_updated' => 'Shan actualitzat els permisos',
'books_empty_contents' => 'No hi ha cap capítol ni cap pàgina en aquest llibre.',
'books_empty_create_page' => 'Crea una pàgina',
'books_empty_sort_current_book' => 'Ordena el llibre',
'books_empty_add_chapter' => 'Afegeix un capítol',
'books_permissions_active' => 'Shan activat els permisos del llibre',
'books_permissions_active' => 'El llibre té permisos',
'books_search_this' => 'Cerca en aquest llibre',
'books_navigation' => 'Navegació pel llibre',
'books_sort' => 'Ordena el contingut del llibre',
'books_sort_desc' => 'Moveu capítols i pàgines dins dun llibre per a reorganitzar-ne el contingut. Podeu afegir altres llibres, la qual cosa permet moure fàcilment capítols i pàgines entre llibres.',
'books_sort_named' => 'Ordena el llibre :bookName',
'books_sort_name' => 'Ordena per nom',
'books_sort_created' => 'Ordena per data de creació',
'books_sort_updated' => 'Ordena per data dactualització',
'books_sort_chapters_first' => 'Els capítols al principi',
'books_sort_chapters_last' => 'Els capítols al final',
'books_navigation' => 'Navegació del llibre',
'books_sort' => 'Ordena el contingut dun llibre',
'books_sort_desc' => 'Moveu els capítols i les pàgines dun llibre per a reorganitzar-ne el contingut. Podeu afegir altres llibres perquè sigui més fàcil moure capítols i pàgines dun llibre a un altre.',
'books_sort_named' => 'Ordena el llibre &laquo;:bookName&raquo;',
'books_sort_name' => 'Ordena pel nom',
'books_sort_created' => 'Ordena per la data de creació',
'books_sort_updated' => 'Ordena per la data dactualització',
'books_sort_chapters_first' => 'Capítols al principi',
'books_sort_chapters_last' => 'Capítols al final',
'books_sort_show_other' => 'Mostra altres llibres',
'books_sort_save' => 'Desa lordre nou',
'books_sort_show_other_desc' => 'Afegiu aquí altres llibres per a inclourels a loperació dordenació i permetre la reorganització entre llibres de manera fàcil.',
'books_sort_move_up' => 'Mou amunt',
'books_sort_move_down' => 'Mou avall',
'books_sort_move_prev_book' => 'Mou al llibre anterior',
'books_sort_move_next_book' => 'Mou al llibre següent',
'books_sort_move_prev_chapter' => 'Mou al capítol anterior',
'books_sort_move_next_chapter' => 'Mou al capítol següent',
'books_sort_move_book_start' => 'Mou a linici del llibre',
'books_sort_move_book_end' => 'Mou al final del llibre',
'books_sort_move_before_chapter' => 'Mou al capítol anterior',
'books_sort_move_after_chapter' => 'Mou al capítol següent',
'books_sort_show_other_desc' => 'Afegiu llibres per a inclourels en lordenació i permetre una reorganització entre llibres més senzilla.',
'books_sort_move_up' => 'Mou-ho cap amunt',
'books_sort_move_down' => 'Mou-ho cap avall',
'books_sort_move_prev_book' => 'Mou-ho al llibre anterior',
'books_sort_move_next_book' => 'Mou-ho al llibre següent',
'books_sort_move_prev_chapter' => 'Mou-ho al capítol anterior',
'books_sort_move_next_chapter' => 'Mou-ho al capítol següent',
'books_sort_move_book_start' => 'Mou-ho al principi del llibre',
'books_sort_move_book_end' => 'Mou-ho al final del llibre',
'books_sort_move_before_chapter' => 'Mou-ho a abans del capítol',
'books_sort_move_after_chapter' => 'Mou-ho a després del capítol',
'books_copy' => 'Copia el llibre',
'books_copy_success' => 'Llibre copiat correctament',
'books_copy_success' => 'Sha copiat el llibre',
// Chapters
'chapter' => 'Capítol',
@@ -174,21 +174,21 @@ return [
'x_chapters' => ':count capítol|:count capítols',
'chapters_popular' => 'Capítols populars',
'chapters_new' => 'Capítol nou',
'chapters_create' => 'Crea un capítol nou',
'chapters_delete' => 'Suprimeix el capítol',
'chapters_delete_named' => 'Suprimeix el capítol :chapterName',
'chapters_delete_explain' => 'Se suprimirà el capítol amb el nom «:chapterName». Totes les pàgines que contingui també se suprimiran.',
'chapters_delete_confirm' => 'Segur que voleu suprimir aquest capítol?',
'chapters_create' => 'Crea un capítol',
'chapters_delete' => 'Suprimeix un capítol',
'chapters_delete_named' => 'Suprimeix el capítol &laquo;:chapterName&raquo;',
'chapters_delete_explain' => 'Se suprimirà el capítol &laquo;:chapterName&raquo;. Se suprimiran totes les pàgines que hi ha al capítol.',
'chapters_delete_confirm' => 'Esteu segur que voleu suprimir aquest capítol?',
'chapters_edit' => 'Edita el capítol',
'chapters_edit_named' => 'Edita el capítol :chapterName',
'chapters_edit_named' => 'Edita el capítol &laquo;:chapterName&raquo;',
'chapters_save' => 'Desa el capítol',
'chapters_move' => 'Mou el capítol',
'chapters_move_named' => 'Mou el capítol :chapterName',
'chapters_move_named' => 'Mou el capítol &laquo;:chapterName&raquo;',
'chapters_copy' => 'Copia el capítol',
'chapters_copy_success' => 'Capítol copiat correctament',
'chapters_copy_success' => 'Sha copiat el capítol',
'chapters_permissions' => 'Permisos del capítol',
'chapters_empty' => 'De moment, aquest capítol no conté cap pàgina.',
'chapters_permissions_active' => 'Shan activat els permisos del capítol',
'chapters_empty' => 'No hi ha cap pàgina en aquest capítol.',
'chapters_permissions_active' => 'El capítol té permisos',
'chapters_permissions_success' => 'Shan actualitzat els permisos del capítol',
'chapters_search_this' => 'Cerca en aquest capítol',
'chapter_sort_book' => 'Ordena el llibre',
@@ -198,105 +198,107 @@ return [
'pages' => 'Pàgines',
'x_pages' => ':count pàgina|:count pàgines',
'pages_popular' => 'Pàgines populars',
'pages_new' => 'Pàgina nova',
'pages_attachments' => 'Adjuncions',
'pages_navigation' => 'Navegació per la pàgina',
'pages_new' => 'Crea una pàgina',
'pages_attachments' => 'Fitxers adjunts',
'pages_navigation' => 'Navegació de la pàgina',
'pages_delete' => 'Suprimeix la pàgina',
'pages_delete_named' => 'Suprimeix la pàgina :pageName',
'pages_delete_draft_named' => 'Suprimeix lesborrany de pàgina :pageName',
'pages_delete_named' => 'Suprimeix la pàgina &laquo;:pageName&raquo;',
'pages_delete_draft_named' => 'Suprimeix lesborrany de pàgina &laquo;:pageName&raquo;',
'pages_delete_draft' => 'Suprimeix lesborrany de pàgina',
'pages_delete_success' => 'Sha suprimit la pàgina',
'pages_delete_draft_success' => 'Sha suprimit lesborrany de pàgina',
'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.',
'pages_delete_confirm' => 'Segur que voleu suprimir aquesta pàgina?',
'pages_delete_draft_confirm' => 'Segur que voleu suprimir aquest esborrany de pàgina?',
'pages_editing_named' => 'Esteu editant :pageName',
'pages_delete_confirm' => 'Esteu segur que voleu suprimir aquesta pàgina?',
'pages_delete_draft_confirm' => 'Esteu segur que voleu suprimir aquest esborrany de pàgina?',
'pages_editing_named' => 'Edició de la pàgina &laquo;:pageName&raquo;',
'pages_edit_draft_options' => 'Opcions desborrany',
'pages_edit_save_draft' => 'Desa lesborrany',
'pages_edit_draft' => 'Edita lesborrany de pàgina',
'pages_editing_draft' => 'Esteu editant lesborrany',
'pages_editing_page' => 'Esteu editant la pàgina',
'pages_edit_draft_save_at' => 'Esborrany desat ',
'pages_editing_draft' => 'Estàs editant lesborrany',
'pages_editing_page' => 'Edita la pàgina',
'pages_edit_draft_save_at' => 'Sha desat lesborrany a les ',
'pages_edit_delete_draft' => 'Suprimeix lesborrany',
'pages_edit_delete_draft_confirm' => 'Segur que voleu suprimir els canvis a lesborrany de pàgina? Es perdran tots els vostres canvis dençà de la darrera vegada que heu desat i sactualitzarà leditor amb lestat de la darrera pàgina desada sense ser un esborrany.',
'pages_edit_delete_draft_confirm' => 'Esteu segur que voleu suprimir els canvis de lesborrany de pàgina? Es perdran tots els canvis que hàgiu fet després de la darrera vegada que vau desar la pàgina i leditor sactualitzarà amb lestat de la darrera vegada que es va desar la pàgina que no era esborrany.',
'pages_edit_discard_draft' => 'Descarta lesborrany',
'pages_edit_switch_to_markdown' => 'Canvia a leditor Markdown',
'pages_edit_switch_to_markdown' => 'Canvia a leditor de Markdown',
'pages_edit_switch_to_markdown_clean' => '(Contingut net)',
'pages_edit_switch_to_markdown_stable' => '(Contingut estable)',
'pages_edit_switch_to_wysiwyg' => 'Canvia a leditor WYSIWYG',
'pages_edit_set_changelog' => 'Defineix el registre de canvis',
'pages_edit_enter_changelog_desc' => 'Introduïu una breu descripció dels canvis que heu fet',
'pages_edit_enter_changelog' => 'Introduïu un registre de canvis',
'pages_editor_switch_title' => 'Canvia deditor',
'pages_editor_switch_are_you_sure' => 'Segur que voleu canviar leditor daquesta pàgina?',
'pages_editor_switch_consider_following' => 'Considereu el següent en canviar deditor:',
'pages_editor_switch_consideration_a' => 'Quan hàgiu desat, lopció del nou editor serà la que utilitzaran tots els futurs editors, incloent-hi els que no poden canviar de tipus deditor amb el seu usuari.',
'pages_editor_switch_consideration_b' => 'En algunes circumstàncies, això pot comportar una pèrdua de detalls i de sintaxi.',
'pages_editor_switch_consideration_c' => 'Els canvis al registre de canvis o a les etiquetes fets dençà de la darrera vegada que sha desat no es mantindran en aquest canvi.',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Alpha Testing)',
'pages_edit_set_changelog' => 'Registre de canvis',
'pages_edit_enter_changelog_desc' => 'Introduïu una descripció breu dels canvis que heu fet',
'pages_edit_enter_changelog' => 'Registra un canvi',
'pages_editor_switch_title' => 'Canvia leditor',
'pages_editor_switch_are_you_sure' => 'Esteu segur que voleu canviar leditor daquesta pàgina?',
'pages_editor_switch_consider_following' => 'Quan canvieu leditor tingueu en compte que:',
'pages_editor_switch_consideration_a' => 'Un cop shagi desat, leditor nou serà el que hagin dutilitzar tots els editor futurs, incloent-hi aquells que no puguin canviar el tipus deditor.',
'pages_editor_switch_consideration_b' => 'És possible que hi hagi pèrdues en el detall i la sintaxi.',
'pages_editor_switch_consideration_c' => 'Els canvis en les etiquetes i el registre de canvis que shagin fet després de la darrera vegada que es va desar no es conservaran.',
'pages_save' => 'Desa la pàgina',
'pages_title' => 'Títol de la pàgina',
'pages_name' => 'Nom de la pàgina',
'pages_md_editor' => 'Editor',
'pages_md_preview' => 'Previsualització',
'pages_md_preview' => 'Visualització prèvia',
'pages_md_insert_image' => 'Insereix una imatge',
'pages_md_insert_link' => 'Insereix un enllaç a una entitat',
'pages_md_insert_drawing' => 'Insereix un diagrama',
'pages_md_show_preview' => 'Mostra la previsualització',
'pages_md_sync_scroll' => 'Sincronitza el desplaçament de la previsualització',
'pages_drawing_unsaved' => 'Sha trobat un diagrama no desat',
'pages_drawing_unsaved_confirm' => 'Shan trobat dades dun diagrama dun intent de desat fallit anterior. Voleu restaurar-les i continuar editant aquest diagrama no desat?',
'pages_not_in_chapter' => 'La pàgina no pertany a cap capítol',
'pages_md_insert_link' => 'Insereix un enllaç dentitat',
'pages_md_insert_drawing' => 'Insereix un dibuix',
'pages_md_show_preview' => 'Mostra la visualització prèvia',
'pages_md_sync_scroll' => 'Sincronitza el desplaçament de la visualització prèvia',
'pages_drawing_unsaved' => 'Sha trobat un dibuix sense desar',
'pages_drawing_unsaved_confirm' => 'Shan trobat dades dun dibuix dun intent anterior de desar un dibuix. Voleu restaurar aquest dibuix no desat per a reprendren ledició?',
'pages_not_in_chapter' => 'La pàgina no és un capítol',
'pages_move' => 'Mou la pàgina',
'pages_copy' => 'Copia la pàgina',
'pages_copy_desination' => 'Destinació de la còpia',
'pages_copy_success' => 'Pàgina copiada correctament',
'pages_copy_desination' => 'Copia la destinació',
'pages_copy_success' => 'Sha copiat la pàgina',
'pages_permissions' => 'Permisos de la pàgina',
'pages_permissions_success' => 'Shan actualitzat els permisos de la pàgina',
'pages_revision' => 'Revisió',
'pages_revisions' => 'Revisions de la pàgina',
'pages_revisions_desc' => 'A continuació hi ha les revisions anteriors de la pàgina. Podeu mirar-les, comparar-les i restaurar-ne versions antigues si us ho permeten els permisos. És possible que lhistorial complet de la pàgina no shi vegi reflectit perquè, depenent de la configuració del sistema, es poden haver esborrat automàticament revisions antigues.',
'pages_revisions_named' => 'Revisions de la pàgina :pageName',
'pages_revision_named' => 'Revisió de la pàgina :pageName',
'pages_revision_restored_from' => 'Restaurada de núm. :id; :summary',
'pages_revisions_created_by' => 'Creada per',
'pages_revisions_desc' => 'A continuació hi ha la llista de totes les revisions daquesta pàgina. Si hi teniu permís, podeu mirar, comparar i restaurar versions antigues de la pàgina. És possible que no hi aparegui tot lhistorial de la pàgina ja que, segons la configuració del sistema, és possible que shagin suprimit les revisions antigues automàticament.',
'pages_revisions_named' => 'Revisions de la pàgina &laquo;:pageName&raquo;',
'pages_revision_named' => 'Revisió de la pàgina &laquo;:pageName&raquo;',
'pages_revision_restored_from' => 'Sha restaurat de núm. :id: :summary',
'pages_revisions_created_by' => 'Revisor:',
'pages_revisions_date' => 'Data de la revisió',
'pages_revisions_number' => 'Núm. ',
'pages_revisions_sort_number' => 'Número de revisió',
'pages_revisions_number' => '#',
'pages_revisions_sort_number' => 'Número de la revisió',
'pages_revisions_numbered' => 'Revisió núm. :id',
'pages_revisions_numbered_changes' => 'Canvis de la revisió núm. :id',
'pages_revisions_editor' => 'Tipus deditor',
'pages_revisions_changelog' => 'Registre de canvis',
'pages_revisions_changes' => 'Canvis',
'pages_revisions_current' => 'Versió actual',
'pages_revisions_preview' => 'Previsualitza',
'pages_revisions_preview' => 'Visualització prèvia',
'pages_revisions_restore' => 'Restaura',
'pages_revisions_none' => 'Aquesta pàgina no té cap revisió',
'pages_copy_link' => 'Copia lenllaç',
'pages_edit_content_link' => 'Vés a la secció a leditor',
'pages_pointer_enter_mode' => 'Activa el mode de selecció de secció',
'pages_pointer_label' => 'Opcions de la secció de la pàgina',
'pages_pointer_permalink' => 'Enllaç permanent de la secció de la pàgina',
'pages_pointer_include_tag' => 'Etiqueta dinclusió de la secció de la pàgina',
'pages_pointer_toggle_link' => 'Mode denllaç permanent, premeu per a mostrar letiqueta dinclusió',
'pages_pointer_toggle_include' => 'Mode detiqueta dinclusió, premeu per a mostrar lenllaç permanent',
'pages_permissions_active' => 'Shan activat els permisos de la pàgina',
'pages_edit_content_link' => 'Ves a la secció a leditor',
'pages_pointer_enter_mode' => 'Entra al mode de selecció de secció',
'pages_pointer_label' => 'Opcions de la secció de pàgina',
'pages_pointer_permalink' => 'Enllaç permanent de la secció de pàgina',
'pages_pointer_include_tag' => 'Etiqueta inclou de la secció de pàgina',
'pages_pointer_toggle_link' => 'Enllaç permanent. Cliqueu per a mostrar letiqueta dinclusió',
'pages_pointer_toggle_include' => 'Etiqueta dinclusió. Cliqueu per a mostrar lenllaç permanent',
'pages_permissions_active' => 'La pàgina té permisos',
'pages_initial_revision' => 'Publicació inicial',
'pages_references_update_revision' => 'Actualització automàtica dels enllaços interns del sistema',
'pages_references_update_revision' => 'Actualització automàtica dels enllaços interns',
'pages_initial_name' => 'Pàgina nova',
'pages_editing_draft_notification' => 'Esteu editant un esborrany que es va desar per darrer cop :timeDiff.',
'pages_draft_edited_notification' => 'Aquesta pàgina sha actualitzat dençà daleshores. Us recomanem que descarteu aquest esborrany.',
'pages_draft_page_changed_since_creation' => 'Aquesta pàgina sha actualitzat dençà que es va crear lesborrany. Us recomanem que descarteu aquest esborrany i que aneu amb compte de no sobreescriure els canvis de la pàgina.',
'pages_editing_draft_notification' => 'Esteu editant un esborrany que es va desar :timeDiff.',
'pages_draft_edited_notification' => 'Des de llavors, sha actualitzat la pàgina. Us recomanem que descarteu aquest esborrany.',
'pages_draft_page_changed_since_creation' => 'Des que es va crear lesborrany, la pàgina sha actualitzat. Us recomanem que descarteu aquest esborrany o que aneu amb compte de no sobreescriure cap canvi.',
'pages_draft_edit_active' => [
'start_a' => ':count usuaris han començat a editar aquesta pàgina',
'start_b' => ':userName ha començat a editar aquesta pàgina',
'time_a' => 'dençà que la pàgina es va actualitzar per darrer cop',
'time_a' => 'des que es va actualitzar aquesta pàgina',
'time_b' => 'en els darrers :minCount minuts',
'message' => ':start :time. Aneu amb compte de no trepitjar-vos les actualitzacions entre vosaltres!',
'message' => ':start :time. Aneu amb compte de no sobreescriure-us els canvis.',
],
'pages_draft_discarded' => 'Sha descartat lesborrany! Sha actualitzat leditor amb el contingut actual de la pàgina',
'pages_draft_deleted' => 'Sha suprimit lesborrany! Sha actualitzat leditor amb el contingut actual de la pàgina',
'pages_specific' => 'Una pàgina específica',
'pages_is_template' => 'Plantilla de pàgina',
'pages_draft_discarded' => 'Sha descartat lesborrany. Leditor sha actualitzat amb el contingut actual de la pàgina',
'pages_draft_deleted' => 'Sha suprimit lesborrany! Leditor sha actualitzat amb el contingut actual de la pàgina',
'pages_specific' => 'Pàgina específica',
'pages_is_template' => 'Plantilla de la pàgina',
// Editor Sidebar
'toggle_sidebar' => 'Commuta la barra lateral',
@@ -306,57 +308,57 @@ return [
'shelf_tags' => 'Etiquetes del prestatge',
'tag' => 'Etiqueta',
'tags' => 'Etiquetes',
'tags_index_desc' => 'Es poden aplicar etiquetes al contingut dins del sistema per a aplicar un mode de categorització flexible. Les etiquetes poden tenir una clau i un valor, però el valor és opcional. Quan shan aplican, el contingut es pot cercar fent servir el nom o el valor de letiqueta.',
'tags_index_desc' => 'Es poden posar etiquetes al contingut per a aconseguir una categorització flexible. Les etiquetes han de tenir una clau i poden o no tenir un valor. Un cop shi han posat, es pot cercar el contingut utilitzant el nom o el valor de les etiquetes.',
'tag_name' => 'Nom de letiqueta',
'tag_value' => 'Valor de letiqueta (opcional)',
'tags_explain' => "Afegiu etiquetes per a categoritzar millor el contingut. \n Podeu assignar un valor a cada etiqueta per a una organització més detallada.",
'tags_add' => 'Afegeix una altra etiqueta',
'tags_remove' => 'Elimina aquesta etiqueta',
'tags_usages' => 'Usos totals de letiqueta',
'tags_assigned_pages' => 'Assignada a pàgines',
'tags_assigned_chapters' => 'Assignada a capítols',
'tags_assigned_books' => 'Assignada a llibres',
'tags_assigned_shelves' => 'Assignada a prestatges',
'tags_explain' => "Poseu alguna etiqueta per a categoritzar millor el contingut. \n Podeu assignar un valor a letiqueta per a aconseguir una organització més detallada.",
'tags_add' => 'Posa-hi una altra etiqueta',
'tags_remove' => 'Lleva aquesta etiqueta',
'tags_usages' => 'Ús total de les etiquetes',
'tags_assigned_pages' => 'Assignades a pàgines',
'tags_assigned_chapters' => 'Assignades a capítols',
'tags_assigned_books' => 'Assignades a llibres',
'tags_assigned_shelves' => 'Assignades a prestatges',
'tags_x_unique_values' => ':count valors únics',
'tags_all_values' => 'Tots els valors',
'tags_view_tags' => 'Mostra les etiquetes',
'tags_view_existing_tags' => 'Mostra les etiquetes existents',
'tags_list_empty_hint' => 'Es poden assignar etiquetes mitjançant la barra lateral de leditor de la pàgina o quan sediten els detalls dun llibre, capítol o prestatge.',
'attachments' => 'Adjuncions',
'attachments_explain' => 'Pugeu fitxers o adjunteu enllaços per a mostrar-los a la pàgina. Són visibles a la barra lateral de la pàgina.',
'attachments_explain_instant_save' => 'Els canvis fets aquí es desen instantàniament.',
'tags_list_empty_hint' => 'Podeu posar etiquetes a la barra lateral de leditor o quan canvieu la informació dun capítol o dun prestatge.',
'attachments' => 'Fitxers adjunts',
'attachments_explain' => 'Pugeu fitxers o afegiu enllaços per a mostrar a la pàgina. Els podreu veure a la barra lateral de la pàgina.',
'attachments_explain_instant_save' => 'Els canvis dels fitxers adjunts es desen a linstant.',
'attachments_upload' => 'Puja un fitxer',
'attachments_link' => 'Adjunta un enllaç',
'attachments_upload_drop' => 'De manera alternativa, podeu arrossegar i deixar anar un fitxer aquí per a pujar-lo com a adjunció.',
'attachments_set_link' => 'Defineix lenllaç',
'attachments_delete' => 'Segur que voleu suprimir aquesta adjunció?',
'attachments_dropzone' => 'Deixeu anar fitxers aquí per a pujar-los',
'attachments_link' => 'Afegeix un enllaç',
'attachments_upload_drop' => 'També podeu arrossegar i deixar anar un fitxer aquí per a adjuntar-lo.',
'attachments_set_link' => 'Configura lenllaç',
'attachments_delete' => 'Esteu segur que voleu suprimir aquest fitxer adjunt?',
'attachments_dropzone' => 'Deixeu-hi anar fitxers per a pujar-los',
'attachments_no_files' => 'No sha pujat cap fitxer',
'attachments_explain_link' => 'Podeu adjuntar un enllaç si preferiu no pujar un fitxer. Pot ser un enllaç a una altra pàgina o un enllaç a un fitxer al núvol.',
'attachments_explain_link' => 'Si us estimeu més no pujar un fitxer, podeu afegir un enllaç. Pot ser un enllaç en una altra pàgina o a un fitxer al núvol.',
'attachments_link_name' => 'Nom de lenllaç',
'attachment_link' => 'Enllaç de ladjunció',
'attachment_link' => 'Enllaç del fitxer adjunt',
'attachments_link_url' => 'Enllaç al fitxer',
'attachments_link_url_hint' => 'URL del lloc o fitxer',
'attachments_link_url_hint' => 'URL del lloc o del fitxer.',
'attach' => 'Adjunta',
'attachments_insert_link' => 'Afegeix un enllaç de ladjunció a la pàgina',
'attachments_insert_link' => 'Afegeix un enllaç de fitxer adjunt a la pàgina',
'attachments_edit_file' => 'Edita el fitxer',
'attachments_edit_file_name' => 'Nom del fitxer',
'attachments_edit_drop_upload' => 'Arrossegueu fitxers o feu clic aquí per a pujar-los i sobreescriurels',
'attachments_order_updated' => 'Sha actualitzat lordre de les adjuncions',
'attachments_updated_success' => 'Shan actualitzat els detalls de les adjuncions',
'attachments_deleted' => 'Sha suprimit ladjunció',
'attachments_file_uploaded' => 'Fitxer pujat correctament',
'attachments_file_updated' => 'Fitxer actualitzat correctament',
'attachments_link_attached' => 'Enllaç adjuntat a la pàgina correctament',
'attachments_edit_drop_upload' => 'Deixeu-hi anar fitxers o cliqueu aquí per a pujar-ne i sobreescriurels.',
'attachments_order_updated' => 'Sha actualitzat lordre del fitxer adjunt',
'attachments_updated_success' => 'Shan actualitzat els detalls dels fitxer adjunt',
'attachments_deleted' => 'Sha suprimit el fitxer adjunt',
'attachments_file_uploaded' => 'Sha pujat el fitxer',
'attachments_file_updated' => 'Sha actualitzat el fitxer',
'attachments_link_attached' => 'Sha adjuntat lenllaç a la pàgina',
'templates' => 'Plantilles',
'templates_set_as_template' => 'La pàgina és una plantilla',
'templates_explain_set_as_template' => 'Podeu definir aquesta pàgina com a plantilla perquè el seu contingut es pugui fer servir en crear altres pàgines. Els altres usuaris podran fer servir la plantilla si tenen permís per a veure aquesta pàgina.',
'templates_explain_set_as_template' => 'Podeu configurar aquesta pàgina com a plantilla perquè sutilitzi en la creació daltres pàgines. Els altres usuaris podran utilitzar aquesta plantilla si tenen permisos de visualització daquesta pàgina.',
'templates_replace_content' => 'Substitueix el contingut de la pàgina',
'templates_append_content' => 'Afegeix al final del contingut de la pàgina',
'templates_prepend_content' => 'Afegeix al principi del contingut de la pàgina',
'templates_append_content' => 'Afegeix-ho després del contingut la pàgina',
'templates_prepend_content' => 'Afegeix-ho abans del contingut la pàgina',
// Profile View
'profile_user_for_x' => 'Usuari fa :time',
'profile_user_for_x' => 'Usuari des de fa :time',
'profile_created_content' => 'Contingut creat',
'profile_not_created_pages' => ':userName no ha creat cap pàgina',
'profile_not_created_chapters' => ':userName no ha creat cap capítol',
@@ -367,71 +369,71 @@ return [
'comment' => 'Comentari',
'comments' => 'Comentaris',
'comment_add' => 'Afegeix un comentari',
'comment_placeholder' => 'Deixeu un comentari aquí',
'comment_count' => '{0} Sense comentaris|{1} 1 comentari|[2,*] :count comentaris',
'comment_placeholder' => 'Deixa un comentari aquí',
'comment_count' => '{0} No hi ha cap comentari|{1} Hi ha 1 comentari|[2,*] Hi ha :count comentaris',
'comment_save' => 'Desa el comentari',
'comment_new' => 'Comentari nou',
'comment_new' => 'Crea un comentari',
'comment_created' => 'ha comentat :createDiff',
'comment_updated' => 'Actualitzat :updateDiff per :username',
'comment_updated' => ':username lha actualitzat :updateDiff',
'comment_updated_indicator' => 'Actualitzat',
'comment_deleted_success' => 'Comentari suprimit',
'comment_created_success' => 'Comentari afegit',
'comment_updated_success' => 'Comentari actualitzat',
'comment_delete_confirm' => 'Segur que voleu suprimir aquest comentari?',
'comment_deleted_success' => 'Sha suprimit el comentari',
'comment_created_success' => 'Sha afegit un comentari',
'comment_updated_success' => 'Sha actualitzat un comentari',
'comment_delete_confirm' => 'Esteu segur que voleu suprimir aquest comentari?',
'comment_in_reply_to' => 'En resposta a :commentId',
'comment_editor_explain' => 'Aquí hi ha els comentaris que shan deixat en aquesta pàgina. Els comentaris es poden afegir i gestionar en veure una pàgina desada.',
'comment_editor_explain' => 'Vet aquí els comentaris que shan fet en aquesta pàgina. Els comentaris es poden fer i gestionar quan es visualitza la pàgina desada.',
// Revision
'revision_delete_confirm' => 'Segur que voleu suprimir aquesta revisió?',
'revision_restore_confirm' => 'Segur que voleu restaurar aquesta revisió? Se substituirà el contingut de la pàgina actual.',
'revision_delete_confirm' => 'Esteu segur que voleu suprimir aquesta revisió?',
'revision_restore_confirm' => 'Esteu segur que voleu restaurar aquesta revisió? Se substituirà el contingut actual de la pàgina.',
'revision_cannot_delete_latest' => 'No es pot suprimir la darrera revisió.',
// Copy view
'copy_consider' => 'Tingueu en compte el següent quan copieu contingut.',
'copy_consider' => 'Quan copieu contingut, tingueu en compte el següent:',
'copy_consider_permissions' => 'No es copiarà la configuració personalitzada de permisos.',
'copy_consider_owner' => 'Esdevindreu el nou propietari de qualsevol contingut copiat.',
'copy_consider_images' => 'Els fitxers dimatge de les pàgines no es duplicaran i les imatges originals mantindran la relació amb la pàgina a la qual es van pujar originalment.',
'copy_consider_attachments' => 'No es copiaran les adjuncions de la pàgina.',
'copy_consider_access' => 'Un canvi dubicació, propietari o permisos pot provocar que aquest contingut esdevingui accessible a públic que abans no nhi tenia.',
'copy_consider_owner' => 'Sereu el propietari de tot el contingut copiat.',
'copy_consider_images' => 'Els fitxers dimatge de la pàgina no es duplicaran i les imatges originals conservaran la relació amb la pàgina a què es van pujar originalment.',
'copy_consider_attachments' => 'No es copiaran els fitxers adjunts de la pàgina.',
'copy_consider_access' => 'És possible que, en canviar la ubicació, el propietari o els permisos, el contingut esdevingui accessible a usuaris que no hi tenien accés.',
// Conversions
'convert_to_shelf' => 'Converteix en prestatge',
'convert_to_shelf_contents_desc' => 'Podeu convertir aquest llibre en un prestatge nou amb el mateix contingut. Els capítols continguts en aquest llibre es convertiran en nous lliures. Si aquest llibre conté pàgines que no pertanyin a cap capítol, aquest llibre canviarà de nom i les contindrà, i aquest llibre esdevindrà part del nou prestatge.',
'convert_to_shelf_permissions_desc' => 'Qualsevol permís definit en aquest llibre es copiarà al nou prestatge i a tots els nous llibres fills que no tinguin permisos explícits. Tingueu en compte que els permisos dels prestatges no shereten automàticament al contingut que continguin de la mateixa manera que passa amb els llibres.',
'convert_to_shelf_contents_desc' => 'Pots convertir aquest llibre en un prestatge nou amb els mateixos continguts. Els capítols daquest llibre es convertiran en llibres. Si aquest llibre conté cap pàgina que no sigui en un capítol, es canviarà el nom del llibre que contindrà aquestes pàgines i el llibre formarà part del prestatge nou.',
'convert_to_shelf_permissions_desc' => 'Es copiaran els permisos daquest llibre al prestatge nou i a tots els llibres que contingui que no tinguin els seus propis permisos. Tingueu en compte que els permisos dels prestatges no es propaguen als seus continguts com sí que ho fan els dels llibres.',
'convert_book' => 'Converteix el llibre',
'convert_book_confirm' => 'Segur que voleu convertir aquest llibre?',
'convert_undo_warning' => 'No es podrà desfer de manera fàcil.',
'convert_book_confirm' => 'Esteu segur que voleu convertir aquest llibre?',
'convert_undo_warning' => 'Això no es pot desfer fàcilment.',
'convert_to_book' => 'Converteix en llibre',
'convert_to_book_desc' => 'Podeu convertir aquest capítol en un llibre nou amb el mateix contingut. Qualsevol permís definit en aquest capítol es copiarà al nou llibre, però qualsevol permís heretat del llibre pare no es copiarà, la qual cosa podria implicar canvis en el control daccés.',
'convert_to_book_desc' => 'Podeu convertir aquest capítol en un llibre nou amb els mateixos continguts. Es copiaran els permisos daquest capítol al llibre nou, però no es copiaran els permisos heretats del llibre a què pertany cosa que podria canviar-ne el control daccés.',
'convert_chapter' => 'Converteix el capítol',
'convert_chapter_confirm' => 'Segur que voleu convertir aquest capítol?',
'convert_chapter_confirm' => 'Esteu segur que voleu convertir aquest capítol?',
// References
'references' => 'Referències',
'references_none' => 'Ni hi ha cap referència detectada a aquest element.',
'references_to_desc' => 'A continuació es llisten tots els continguts coneguts del sistema que enllacen a aquest element.',
'references_none' => 'No hi ha cap referència cap a aquest element.',
'references_to_desc' => 'A la llista que hi ha a continuació, hi trobareu tot el contingut que enllaça cap aquest element.',
// Watch Options
'watch' => 'Segueix',
'watch_title_default' => 'Preferències per defecte',
'watch_desc_default' => 'Restableix el seguiment a només les preferències de notificació per defecte.',
'watch_title_default' => 'Preferències predeterminades',
'watch_desc_default' => 'Reverteix el seguiment a les preferències de notificació per defecte.',
'watch_title_ignore' => 'Ignora',
'watch_desc_ignore' => 'Ignora totes les notificacions, incloent-hi les preferències a nivell dusuari.',
'watch_desc_ignore' => 'Ignora totes les notificacions, incloent-hi les de les preferències de nivell usuari.',
'watch_title_new' => 'Pàgines noves',
'watch_desc_new' => 'Notificam quan es creï qualsevol pàgina nova dins daquest element.',
'watch_title_updates' => 'Totes les actualitzacions de pàgines',
'watch_desc_updates' => 'Notificam totes les pàgines noves i qualsevol canvi de pàgina.',
'watch_desc_updates_page' => 'Notificam tots els canvis de pàgina.',
'watch_title_comments' => 'Totes les actualitzacions de pàgines i comentaris',
'watch_desc_comments' => 'Notificam totes les pàgines noves, qualsevol canvi de pàgina i comentaris nous.',
'watch_desc_comments_page' => 'Notificam qualsevol canvi de pàgina i comentaris nous.',
'watch_change_default' => 'Canvieu les preferències de notificació per deecte',
'watch_detail_ignore' => 'Signoren les notificacions',
'watch_detail_new' => 'Se segueixen pàgines noves',
'watch_detail_updates' => 'Se segueixen pàgines noves i actualitzacions',
'watch_detail_comments' => 'Se segueixen pàgines noves, actualitzacions i comentaris',
'watch_detail_parent_book' => 'Se segueix mitjançant el llibre pare',
'watch_detail_parent_book_ignore' => 'Signora mitjançant el llibre pare',
'watch_detail_parent_chapter' => 'Se segueix mitjançant el capítol pare',
'watch_detail_parent_chapter_ignore' => 'Signora mitjançant el capítol pare',
'watch_desc_new' => 'Notificam la creació duna pàgina nova dins daquest element.',
'watch_title_updates' => 'Actualitzacions de la pàgina',
'watch_desc_updates' => 'Notificam totes les pàgines noves i totes les actualitzacions de les pàgines.',
'watch_desc_updates_page' => 'Notificam totes les actualitzacions de les pàgines.',
'watch_title_comments' => 'Actualitzacions i comentaris de la pàgina',
'watch_desc_comments' => 'Notificam totes les pàgines noves, totes les actualitzacions de les pàgines i tots els comentaris nous.',
'watch_desc_comments_page' => 'Notificam tots les actualitzacions de pàgines i tots els comentaris nous.',
'watch_change_default' => 'Canvia les preferències de notificació per defecte',
'watch_detail_ignore' => 'Sestan ignorant les notificacions',
'watch_detail_new' => 'Sestà fent el seguiment de pàgines noves',
'watch_detail_updates' => 'Sestà fent el seguiment de pàgines noves i les actualitzacions de les pàgines',
'watch_detail_comments' => 'Sestà fent el seguiment de pàgines noves i les actualitzacions de les pàgines i els comentaris',
'watch_detail_parent_book' => 'Sestà fent el seguiment a través del llibre de què és part',
'watch_detail_parent_book_ignore' => 'Sestà ignorant a través del llibre de què és part',
'watch_detail_parent_chapter' => 'Sestà fent el seguiment a través del capítol de què és part',
'watch_detail_parent_chapter_ignore' => 'Sestà ignorant a través del capítol de què és part',
];

View File

@@ -6,114 +6,116 @@ return [
// Permissions
'permission' => 'No teniu permís per a accedir a la pàgina sol·licitada.',
'permissionJson' => 'No teniu permís per a executar lacció sol·licitada.',
'permissionJson' => 'No teniu permís per a fer lacció sol·licitada.',
// Auth
'error_user_exists_different_creds' => 'Ja hi ha un usuari amb ladreça electrònica :email però amb credencials diferents.',
'error_user_exists_different_creds' => 'Ja existeix un usuari amb el correu electrònic :email però amb unes credencials diferents.',
'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details',
'email_already_confirmed' => 'Ladreça electrònica ja està confirmada. Proveu diniciar la sessió.',
'email_already_confirmed' => 'Ja sha confirmat el correu electrònic. Proveu diniciar sessió.',
'email_confirmation_invalid' => 'Aquest testimoni de confirmació no és vàlid o ja sha utilitzat. Proveu de tornar-vos a registrar.',
'email_confirmation_expired' => 'El testimoni de confirmació ha caducat. Sha enviat un nou correu electrònic de confirmació.',
'email_confirmation_awaiting' => 'Cal confirmar ladreça electrònica del compte que utilitzeu',
'ldap_fail_anonymous' => 'Laccés a lLDAP ha fallat fent servir un lligam anònim',
'ldap_fail_authed' => 'Laccés a lLDAP ha fallat fent servir els detalls de DN i contrasenya proporcionats',
'ldap_extension_not_installed' => 'Lextensió de lLDAP del PHP no està instal·lada',
'ldap_cannot_connect' => 'No sha pogut connectar amb el servidor de lLDAP, la connexió inicial ha fallat',
'saml_already_logged_in' => 'Ja heu iniciat la sessió',
'saml_no_email_address' => 'No sha pogut trobar cap adreça electrònica per a aquest usuari en les dades proporcionades pel sistema dautenticació extern',
'saml_invalid_response_id' => 'La petició del sistema dautenticació extern no és reconeguda per un procés iniciat per aquesta aplicació. Aquest problema podria ser causat per navegar endarrere després diniciar la sessió.',
'saml_fail_authed' => 'Linici de sessió fent servir :system ha fallat, el sistema no ha proporcionat una autorització satisfactòria',
'oidc_already_logged_in' => 'Ja teniu una sessió iniciada',
'oidc_no_email_address' => 'No sha pogut trobar cap adreça electrònica per a aquest usuari en les dades proporcionades pel sistema dautenticació extern',
'oidc_fail_authed' => 'Linici de sessió fent servir :system ha fallat, el sistema no ha proporcionat una autorització satisfactòria',
'social_no_action_defined' => 'No hi ha cap acció definida',
'social_login_bad_response' => "Sha rebut un error mentre siniciava la sessió amb :socialAccount: \n:error",
'social_account_in_use' => 'Aquest compte de :socialAccount ja està en ús, proveu diniciar la sessió mitjançant lopció de :socialAccount.',
'social_account_email_in_use' => 'Ladreça electrònica :email ja està en ús. Si ja teniu un compte, podeu connectar-hi el vostre compte de :socialAccount a la configuració del vostre perfil.',
'email_confirmation_expired' => 'Aquest testimoni de confirmació ha caducat. Sha enviat un altre correu electrònic de confirmació.',
'email_confirmation_awaiting' => 'Cal confirmar ladreça electrònica del compte que utilitzeu.',
'ldap_fail_anonymous' => 'Laccés LDAP anònim ha fallat',
'ldap_fail_authed' => 'Laccés LDAP amb el nom distintiu i la contrasenya proporcionades',
'ldap_extension_not_installed' => 'Lextensió PHP de lLDAP no està instal·lada',
'ldap_cannot_connect' => 'No sha pogut connectar amb el servidor LDAP perquè la connexió inicial ha fallat',
'saml_already_logged_in' => 'Ja heu iniciat sessió',
'saml_no_email_address' => 'No sha pogut trobar una adreça electrònica per a aquest usuari a les dades proporcionades pel sistema dautenticació extern',
'saml_invalid_response_id' => 'Un procés iniciat per aquesta aplicació no reconeix la sol·licitud del sistema dautenticació extern. Haver navegat enrere després diniciar sessió en podria ser la causa.',
'saml_fail_authed' => 'No sha pogut iniciar sessió amb :system perquè el sistema no ha proporcionat una autorització satisfactòria',
'oidc_already_logged_in' => 'Ja heu iniciat sessió',
'oidc_no_email_address' => 'No sha pogut trobar una adreça electrònica per a aquest usuari a les dades proporcionades pel sistema dautenticació extern',
'oidc_fail_authed' => 'No sha pogut iniciar sessió amb :system perquè el sistema no ha proporcionat una autorització satisfactòria',
'social_no_action_defined' => 'No sha definit cap acció',
'social_login_bad_response' => "Sha produït un error en linici de sessió amb :socialAccount: \n:error",
'social_account_in_use' => 'Aquest compte de :socialAccount ja sestà utilitzant. Proveu diniciar sessió amb :socialAccount.',
'social_account_email_in_use' => 'Ladreça electrònica :email ja sestà utilitzant. Si ja teniu uns compte podeu connectar-hi el vostre compte de :socialAccount des de la configuració del vostre perfil.',
'social_account_existing' => 'Aquest compte de :socialAccount ja està associat al vostre perfil.',
'social_account_already_used_existing' => 'Aquest compte de :socialAccount ja el fa servir un altre usuari.',
'social_account_already_used_existing' => 'Aquest compte de :socialAccount ja està associat a un altre usuari.',
'social_account_not_used' => 'Aquest compte de :socialAccount no està associat a cap usuari. Associeu-lo a la configuració del vostre perfil. ',
'social_account_register_instructions' => 'Si encara no teniu cap compte, podeu registrar-vos fent servir lopció de :socialAccount.',
'social_account_register_instructions' => 'Si encara no teniu un compte, podeu registrar-vos amb lopció :socialAccount.',
'social_driver_not_found' => 'No sha trobat el controlador social',
'social_driver_not_configured' => 'La configuració social de :socialAccount no és correcta.',
'invite_token_expired' => 'Aquest enllaç dinvitació ha caducat. Podeu provar de restablir la contrasenya del vostre compte.',
'social_driver_not_configured' => 'La configuració de :socialAccount no és correcta.',
'invite_token_expired' => 'Aquest enllaç dinvitació ha caducat. Proveu de reinicialitzar la contrasenya.',
'login_user_not_found' => 'A user for this action could not be found.',
// System
'path_not_writable' => 'No sha pogut pujar al camí del fitxer :filePath. Assegureu-vos que el servidor hi té permisos descriptura.',
'cannot_get_image_from_url' => 'No sha pogut obtenir la imatge de :url',
'cannot_create_thumbs' => 'El servidor no pot crear miniatures. Reviseu que tingueu instal·lada lextensió GD del PHP.',
'server_upload_limit' => 'El servidor no permet pujades daquesta mida. Proveu-ho amb una mida de fitxer més petita.',
'server_post_limit' => 'El servidor no pot rebre la quantitat de dades que heu proporcionat. Torneu-ho a provar amb menys dades o un fitxer més petit.',
'uploaded' => 'El servidor no permet pujades daquesta mida. Proveu-ho amb una mida de fitxer més petita.',
'path_not_writable' => 'No sha pogut pujar a :filePath. Assegureu-vos que teniu permisos descriptura al servidor.',
'cannot_get_image_from_url' => 'No sha pogut obtenir la imatge des de :url',
'cannot_create_thumbs' => 'El servidor no pot crear miniatures. Assegureu-vos que sha instal·lat lextensió de GD PHP.',
'server_upload_limit' => 'El servidor no permet pujades daquesta mida. Proveu-ho amb una mida més petita.',
'server_post_limit' => 'El servidor no pot rebre la quantitat de dades proporcionades. Proveu-ho amb menys dades o amb un fitxer més petit.',
'uploaded' => 'El servidor no permet pujades daquesta mida. Proveu-ho amb un fitxer més petit.',
// Drawing & Images
'image_upload_error' => 'Sha produït un error en pujar la imatge',
'image_upload_type_error' => 'El tipus dimatge que heu pujat no és vàlid',
'image_upload_replace_type' => 'Les substitucions de fitxers dimatge han de ser el mateix tipus',
'image_upload_memory_limit' => 'No sha pogut gestionar la pujada de la imatge i/o crear-ne miniatures a causa dels límits dels recursos del sistema.',
'image_thumbnail_memory_limit' => 'No sha pogut crear les variacions de mida de la imatge a causa dels límits dels recursos del sistema.',
'image_upload_error' => 'Sha produït un error en pujar la imatge.',
'image_upload_type_error' => 'El tipus dimatge no és vàlid.',
'image_upload_replace_type' => 'Els fitxers dimatge shan de substituir per un del mateix tipus',
'image_upload_memory_limit' => 'No sha pogut pujar la imatge o crear-ne les miniatures a causa dels límits dels recursos del sistema.',
'image_thumbnail_memory_limit' => 'No shan pogut crear mides dimatge diferents a causa dels límits dels recursos del sistema.',
'image_gallery_thumbnail_memory_limit' => 'No shan pogut crear les miniatures de la galeria a causa dels límits dels recursos del sistema.',
'drawing_data_not_found' => 'No shan pogut carregar les dades de dibuix. És possible que el fitxer de dibuix ja no existeixi o que no tingueu permisos per a accedir-hi.',
'drawing_data_not_found' => 'No shan pogut pujar les dades del dibuix. És possible que el fitxer del dibuix ja no existeixi o que no tingueu permís per a accedir-hi.',
// Attachments
'attachment_not_found' => 'No sha trobat ladjunció',
'attachment_upload_error' => 'Sha produït un error en pujar el fitxer de ladjunció',
'attachment_not_found' => 'No sha trobat el fitxer adjunt',
'attachment_upload_error' => 'Sha produït un error en pujar el fitxer adjunt',
// Pages
'page_draft_autosave_fail' => 'No sha pogut desar lesborrany. Assegureu-vos que teniu connexió a Internet abans de desar la pàgina',
'page_draft_delete_fail' => 'No sha pogut suprimir lesborrany de la pàgina i obtenir el contingut desat actual de la pàgina',
'page_custom_home_deletion' => 'No es pot suprimir una pàgina mentre estigui definida com a pàgina dinici',
'page_draft_autosave_fail' => 'No sha pogut desar lesborrany. Assegureu-vos que esteu connectat a internet per a desar la pàgina.',
'page_draft_delete_fail' => 'No sha pogut suprimir lesborrany de la pàgina i obtenir el contingut de la pàgina desada actual.',
'page_custom_home_deletion' => 'No es pot suprimir una pàgina que està definida com a pàgina dinici.',
// Entities
'entity_not_found' => 'No sha trobat lentitat',
'bookshelf_not_found' => 'No sha trobat el prestatge',
'book_not_found' => 'No sha trobat el llibre',
'page_not_found' => 'No sha trobat la pàgina',
'chapter_not_found' => 'No sha trobat el capítol',
'selected_book_not_found' => 'No sha trobat el llibre seleccionat',
'selected_book_chapter_not_found' => 'No sha trobat el llibre o el capítol seleccionat',
'guests_cannot_save_drafts' => 'Els convidats no poden desar esborranys',
'entity_not_found' => 'No sha trobat lentitat.',
'bookshelf_not_found' => 'No sha trobat el prestatge.',
'book_not_found' => 'No sha trobat el llibre.',
'page_not_found' => 'No sha trobat la pàgina.',
'chapter_not_found' => 'No sha trobat el capítol.',
'selected_book_not_found' => 'No sha trobat el llibre seleccionat.',
'selected_book_chapter_not_found' => 'No sha trobat el llibre o el capítol seleccionat.',
'guests_cannot_save_drafts' => 'Els convidats no poden desar esborranys.',
// Users
'users_cannot_delete_only_admin' => 'No podeu suprimir lúnic administrador',
'users_cannot_delete_guest' => 'No podeu suprimir lusuari convidat',
'users_cannot_delete_only_admin' => 'No podeu suprimir ladministrador únic.',
'users_cannot_delete_guest' => 'No podeu suprimir lusuari convidat.',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send',
// Roles
'role_cannot_be_edited' => 'Aquest rol no es pot editar',
'role_system_cannot_be_deleted' => 'Aquest rol és un rol del sistema i no es pot suprimir',
'role_registration_default_cannot_delete' => 'No es pot suprimir aquest rol mentre estigui definit com a rol per defecte dels registres',
'role_cannot_remove_only_admin' => 'Aquest usuari és lúnic usuari assignat al rol dadministrador. Assigneu el rol dadministrador a un altre usuari abans de provar de suprimir aquest.',
'role_cannot_be_edited' => 'No es pot editar aquest rol.',
'role_system_cannot_be_deleted' => 'No es pot suprimir aquest rol perquè és un rol del sistema.',
'role_registration_default_cannot_delete' => 'No es pot suprimir aquest rol mentre estigui configurat com a rol de registre per defecte.',
'role_cannot_remove_only_admin' => 'Aquest és lúnic usuari assignat al rol dadministrador. Assigneu el rol dadministrador a un altre usuari abans de provar de suprimir-lo.',
// Comments
'comment_list' => 'Sha produït un error en obtenir els comentaris.',
'cannot_add_comment_to_draft' => 'No podeu afegir comentaris a un esborrany.',
'cannot_add_comment_to_draft' => 'No es poden afegir comentaris en un esborrany.',
'comment_add' => 'Sha produït un error en afegir o actualitzar el comentari.',
'comment_delete' => 'Sha produït un error en suprimir el comentari.',
'empty_comment' => 'No podeu afegir un comentari buit.',
'empty_comment' => 'No es pot afegir un comentari buit.',
// Error pages
'404_page_not_found' => 'No sha trobat la pàgina',
'sorry_page_not_found' => 'No hem pogut trobar la pàgina que cerqueu.',
'sorry_page_not_found_permission_warning' => 'Si esperàveu que existís, és possible que no tingueu permisos per a veure-la.',
'sorry_page_not_found' => 'No sha trobat la pàgina que heu cercat.',
'sorry_page_not_found_permission_warning' => 'Si la pàgina existeix, és possible que no tingueu permís per a accedir-hi.',
'image_not_found' => 'No sha trobat la imatge',
'image_not_found_subtitle' => 'No ha estat possible trobar el fitxer de la imatge que cerqueu.',
'image_not_found_details' => 'Si esperàveu que existís, és possible que shagi suprimit.',
'return_home' => 'Torna a linici',
'error_occurred' => 'Sha produït un error',
'app_down' => ':appName està fora de servei en aquests moments',
'back_soon' => 'Tornarà a estar disponible aviat.',
'image_not_found_subtitle' => 'No sha trobat la imatge que heu cercat.',
'image_not_found_details' => 'És possible que shagi suprimit.',
'return_home' => 'Torna a la pàgina dinici',
'error_occurred' => 'Sha produït un error.',
'app_down' => ':appName està fora de servei.',
'back_soon' => 'Aviat ho arreglarem.',
// API errors
'api_no_authorization_found' => 'No sha trobat cap testimoni dautorització a la petició',
'api_bad_authorization_format' => 'Sha trobat un testimoni dautorització a la petició, però el format sembla erroni',
'api_user_token_not_found' => 'No sha trobat cap testimoni de lAPI per al testimoni dautorització proporcionat',
'api_incorrect_token_secret' => 'El secret proporcionat per al testimoni de lAPI proporcionat és incorrecte',
'api_user_no_api_permission' => 'El propietari del testimoni de lAPI utilitzat no té permís per a fer crides a lAPI',
'api_user_token_expired' => 'El testimoni dautorització utilitzat ha caducat',
'api_no_authorization_found' => 'No sha trobat cap testimoni dautorització en aquesta sol·licitud.',
'api_bad_authorization_format' => 'Sha trobat un testimoni dautorització en aquesta sol·licitud però no tenia el format correcte.',
'api_user_token_not_found' => 'No sha trobat cap testimoni dAPI per al testimoni dautorització proporcionat.',
'api_incorrect_token_secret' => 'El secret proporcionat per al testimoni dAPI utilitzat no és correcte.',
'api_user_no_api_permission' => 'El propietari del testimoni API utilitzat no té permís per a fer crides a lAPI.',
'api_user_token_expired' => 'El testimoni dautorització utilitzat ha caducat.',
// Settings & Maintenance
'maintenance_test_email_failure' => 'Sha produït un error en enviar un correu electrònic de prova:',
'maintenance_test_email_failure' => 'Sha produït un error en enviar el correu electrònic de prova:',
// HTTP errors
'http_ssr_url_no_match' => 'LURL no coincideix amb els amfitrions SSR permesos segons la configuració',
'http_ssr_url_no_match' => 'LURL no coincideix amb els amfitrions SSR configurats permesos.',
];

View File

@@ -4,24 +4,24 @@
*/
return [
'new_comment_subject' => 'Comentari nou a la pàgina :pageName',
'new_comment_intro' => 'Un usuari ha comentat en una pàgina de :appName:',
'new_page_subject' => 'Pàgina nova: :pageName',
'new_page_intro' => 'Sha creat una pàgina nova a :appName:',
'updated_page_subject' => 'Pàgina actualitzada: :pageName',
'updated_page_intro' => 'Sha actualitzat una pàgina a :appName:',
'updated_page_debounce' => 'Per a evitar les notificacions massives, no us enviarem notificacions si hi ha més edicions en aquesta pàgina fetes pel mateix editor.',
'new_comment_subject' => 'Sha fet un comentari a la pàgina :pageName.',
'new_comment_intro' => 'Sha fet un comentari nou en una pàgina a :appName.',
'new_page_subject' => 'Sha creat la pàgina :pageName',
'new_page_intro' => 'Sha creat una pàgina nova a :appName.',
'updated_page_subject' => 'Sha actualitzat la pàgina :pageName',
'updated_page_intro' => 'Sha actualitzat una pàgina a :appName.',
'updated_page_debounce' => 'Per a evitar que sacumulin les notificacions, durant un temps no se us notificarà cap canvi fet en aquesta pàgina pel mateix usuari.',
'detail_page_name' => 'Nom de la pàgina:',
'detail_page_path' => 'Camí de la pàgina:',
'detail_commenter' => 'Autor del comentari:',
'detail_comment' => 'Comentari:',
'detail_created_by' => 'Creat per:',
'detail_updated_by' => 'Actualitzat per:',
'detail_created_by' => 'Creada per:',
'detail_updated_by' => 'Actualitzada per:',
'action_view_comment' => 'Mostra el comentari',
'action_view_page' => 'Mostra la pàgina',
'footer_reason' => 'Rebeu aquesta notificació perquè :link cobreixen aquest tipus dactivitat en aquest element.',
'footer_reason_link' => 'les vostres preferències de notificacions',
'footer_reason' => 'Heu rebut aquesta notificació perquè :link inclouen aquest tipus dactivitat per a aquest element.',
'footer_reason_link' => 'les vostres preferències de notificació',
];

View File

@@ -6,10 +6,10 @@
*/
return [
'password' => 'Les contrasenyes han de tenir almenys vuit caràcters i coincidir amb la confirmació.',
'user' => "No sha trobat cap usuari amb aquesta adreça electrònica.",
'password' => 'Les contrasenyes han de tenir almenys 8 caràcters i han de coincidir amb la confirmació de contrasenya.',
'user' => "No es pot trobar un usuari amb aquesta adreça electrònica.",
'token' => 'El testimoni de restabliment de la contrasenya no és vàlid per a aquesta adreça electrònica.',
'sent' => 'Us hem enviat un enllaç per a restablir la contrasenya!',
'reset' => 'Sha restablert la contrasenya!',
'reset' => 'Sha reinicialitzat la contrasenya.',
];

View File

@@ -7,45 +7,45 @@
return [
'my_account' => 'El meu compte',
'shortcuts' => 'Dreceres',
'shortcuts_interface' => 'Preferències de les dreceres de la interfície dusuari',
'shortcuts_toggle_desc' => 'Aquí podeu activar o desactivar les dreceres de teclat de la interfície del sistema, que sutilitzen per a la navegació i les accions.',
'shortcuts_customize_desc' => 'Podeu personalitzar cadascuna de les dreceres següents. Premeu la combinació de tecles desitjada després de seleccionar la casella duna drecera.',
'shortcuts_toggle_label' => 'Dreceres de teclat activades',
'shortcuts' => 'Dreceres de teclat',
'shortcuts_interface' => 'Preferències de dreceres de teclat de la interfície dusuari',
'shortcuts_toggle_desc' => 'Configureu les dreceres de teclat de la interfície dusuari del sistema utilitzades per a la navegació i les accions.',
'shortcuts_customize_desc' => 'Podeu personalitzar cadascuna de les dreceres a continuació. seleccionant el camp dentrada de cada drecera i prement la combinació de tecles que vulgueu.',
'shortcuts_toggle_label' => 'Activa les dreceres de teclat',
'shortcuts_section_navigation' => 'Navegació',
'shortcuts_section_actions' => 'Accions habituals',
'shortcuts_save' => 'Desa les dreceres',
'shortcuts_overlay_desc' => 'Nota: Quan sactiven les dreceres, hi ha una interfície dajuda disponible en prémer «?» que destaca les dreceres disponibles per a accions visibles actualment a la pantalla.',
'shortcuts_update_success' => 'Shan actualitzat les preferències de les dreceres!',
'shortcuts_overview_desc' => 'Gestioneu les dreceres de teclat que podeu utilitzar per a navegar per la interfície dusuari del sistema.',
'shortcuts_overlay_desc' => 'Nota: Quan les dreces estan activades hi haurà disponible una ajuda que es pot obtenir prement &laquo;?&raquo; que ressaltarà les dreceres disponibles a la pantalla que sestigui visualitzant.',
'shortcuts_update_success' => 'Shan actualitzat les preferències de drecera.',
'shortcuts_overview_desc' => 'Gestioneu les dreceres que sutilitzen per a navegar per la interfície dusuari.',
'notifications' => 'Preferències de notificació',
'notifications_desc' => 'Controleu les notificacions per correu electrònic que rebeu quan es fan certes activitats dins del sistema.',
'notifications_opt_own_page_changes' => 'Notificam quan hi hagi canvis a pàgines de les quals sóc propietari',
'notifications_opt_own_page_comments' => 'Notificam quan hi hagi comentaris a pàgines de les quals sóc propietari',
'notifications_opt_comment_replies' => 'Notificam quan hi hagi respostes als meus comentaris',
'notifications' => 'Preferències de les notificacions',
'notifications_desc' => 'Gestioneu les notificacions de correu electrònic que rebreu quan es facin certes activitats.',
'notifications_opt_own_page_changes' => 'Notificam els canvis a les meves pàgines.',
'notifications_opt_own_page_comments' => 'Notificam la creació de comentaris a les meves pàgines.',
'notifications_opt_comment_replies' => 'Notificam les respostes als meus comentaris.',
'notifications_save' => 'Desa les preferències',
'notifications_update_success' => 'Shan actualitzat les preferències de notificacions!',
'notifications_update_success' => 'Shan actualitzat les preferències de notificació',
'notifications_watched' => 'Elements seguits i ignorats',
'notifications_watched_desc' => 'A continuació hi ha els elements que tenen aplicades preferències de seguiment personalitzades. Per a actualitzar-ne les preferències, consulteu lelement i seleccioneu les opcions de seguiment a la barra lateral.',
'notifications_watched_desc' => 'A continuació hi ha els elements que tenen preferències de seguiment personalitzades. Per a actualitzar-les, accediu a lelement i configureu-les a la barra lateral.',
'auth' => 'Accés i seguretat',
'auth_change_password' => 'Canvia la contrasenya',
'auth_change_password_desc' => 'Canvieu la contrasenya que feu servir per a iniciar la sessió a laplicació. Cal que tingui un mínim de 8 caràcters.',
'auth_change_password_success' => 'Sha actualitzat la contrasenya!',
'auth_change_password_desc' => 'Canvieu la contrasenya que sutilitzarà per a iniciar sessió a laplicació. Ha de tenir com a mínim 8 caràcters.',
'auth_change_password_success' => 'Sha actualitzat la contrasenya.',
'profile' => 'Detalls del perfil',
'profile_desc' => 'Gestioneu els detalls del vostre compte, que us representa davant daltres usuaris, i també els detalls que sutilitzen per a la comunicació i la personalització del sistema.',
'profile_view_public' => 'Mostra el perfil públic',
'profile_name_desc' => 'Configureu el vostre nom públic, que és visible als altres usuaris del sistema a través de les activitats que realitzeu i del contingut del qual sou propietari.',
'profile_email_desc' => 'Aquest correu sutilitza per a notificacions i, depenent de lautenticació activa del sistema, per a laccés al sistema.',
'profile_email_no_permission' => 'Malauradament, no teniu permisos per a canviar ladreça electrònica. Si voleu canviar-la, caldrà que demaneu a un administrador que us faci el canvi.',
'profile_avatar_desc' => 'Seleccioneu una imatge que us representarà davant daltres usuaris del sistema. Idealment, aquesta imatge hauria de ser un quadrat de 256 px dalçada i damplada.',
'profile_admin_options' => 'Opcions dadministració',
'profile_admin_options_desc' => 'Podeu trobar opcions de nivell dadministració addicionals del vostre compte, com ara les que permeten gestionar les assignacions de rols, a làrea de laplicació «Configuració > Usuaris».',
'profile' => 'Informació del perfil',
'profile_desc' => 'Gestioneu la informació del vostre compte que representa com us veuran els altres usuaris a més de la informació que sutilitza per a la comunicació i la personalització del sistema.',
'profile_view_public' => 'Mostram el perfil públic',
'profile_name_desc' => 'Configureu el nom públic que veuran els altres usuaris a les activitats que feu i al vostre contingut.',
'profile_email_desc' => 'Aquesta serà ladreça electrònica on senviaran les notificacions i que, segons lautenticació del sistema activada, sutilitzarà per a iniciar sessió.',
'profile_email_no_permission' => 'No teniu permís per a canviar la vostra adreça electrònica. Si voleu canviar-la, demaneu-ho a un administrador.',
'profile_avatar_desc' => 'Seleccioneu una imatge que us representi davant dels altres usuaris. És millor que sigui una imatge quadrada de 256px de costat.',
'profile_admin_options' => 'Preferències dadministrador',
'profile_admin_options_desc' => 'Podeu trobar preferències addicionals dadministrador pel vostre compte, com ara les que gestionen els rols dusuari, a &laquo;Preferències &rsaquo; Usuaris&raquo;.',
'delete_account' => 'Suprimeix el compte',
'delete_my_account' => 'Suprimeix el meu compte',
'delete_my_account_desc' => 'Se suprimirà completament el vostre compte dusuari del sistema. No podreu recuperar aquest compte ni revertir aquesta acció. El contingut que hàgiu creat, com ara les pàgines creades o les imatges pujades, es mantindrà.',
'delete_my_account_warning' => 'Segur que voleu suprimir el vostre compte?',
'delete_my_account_desc' => 'Se suprimirà completament del sistema el vostre compte dusuari. No podreu recuperar el compte ni revertir-ne la supressió. Es conservarà el contingut que hàgiu creat, com ara les pàgines o les imatges que hàgiu pujat.',
'delete_my_account_warning' => 'Esteu segur que voleu suprimir el vostre compte?',
];

View File

@@ -8,119 +8,119 @@ return [
// Common Messages
'settings' => 'Configuració',
'settings_save' => 'Desa la configuració',
'system_version' => 'Versió del sistema',
'settings_save' => 'Configuració de desat',
'system_version' => 'Versió de sistema',
'categories' => 'Categories',
// App Settings
'app_customization' => 'Personalització',
'app_features_security' => 'Funcionalitats i seguretat',
'app_features_security' => 'Funcions i seguretat',
'app_name' => 'Nom de laplicació',
'app_name_desc' => 'Aquest nom es mostra a la capçalera i en tots els correus electrònics enviats pel sistema.',
'app_name_desc' => 'El nom es mostra a la capçalera i als correus electrònics enviats pel sistema.',
'app_name_header' => 'Mostra el nom a la capçalera',
'app_public_access' => 'Accés públic',
'app_public_access_desc' => 'Si activeu aquesta opció, es permetrà que els visitants que no hagin iniciat la sessió accedeixin al contingut de la vostra instància del BookStack.',
'app_public_access_desc_guest' => 'Podeu controlar laccés dels visitants públics amb lusuari «Convidat».',
'app_public_access_desc' => 'Si activeu aquesta opció les visitants podran accedir a la vostra instància del BookStack sense iniciar sessió.',
'app_public_access_desc_guest' => 'Laccés per als visitants públics es pot gestionar amb lusuari &laquo;Convidat&raquo;.',
'app_public_access_toggle' => 'Permet laccés públic',
'app_public_viewing' => 'Voleu permetre la visualització pública?',
'app_secure_images' => 'Pujades dimatges amb més seguretat',
'app_secure_images_toggle' => 'Activa les pujades dimatges amb més seguretat',
'app_secure_images_desc' => 'Per motius de rendiment, totes les imatges són públiques. Aquesta opció afegeix una cadena aleatòria i difícil dendevinar al davant dels URL dimatges. Assegureu-vos que els índexs de directoris no estiguin activats per a evitar-hi laccés de manera fàcil.',
'app_public_viewing' => 'Esteu segur que voleu permetre laccés públic?',
'app_secure_images' => 'Pujada dimatges amb més seguretat',
'app_secure_images_toggle' => 'Habilita la pujada dimatges amb més seguretat',
'app_secure_images_desc' => 'Per raons de rendiment, totes les imatges són públiques. Aquesta opció afegeix una cadena aleatòria que és difícil dendevinar al davant de lURL de les imatges. Assegureu-vos que els índex de carpetes estan desactivats perquè no shi pugui accedir fàcilment.',
'app_default_editor' => 'Editor de pàgines per defecte',
'app_default_editor_desc' => 'Seleccioneu quin editor es farà servir per defecte en editar pàgines noves. Es pot canviar a nivell de pàgina si els permisos ho permeten.',
'app_default_editor_desc' => 'Seleccioneu quin editor sutilitzarà per defecte quan seditin pàgines noves. Això es pot sobreescriure a nivell de pàgina si els permisos ho permeten.',
'app_custom_html' => 'Contingut personalitzat a la capçalera HTML',
'app_custom_html_desc' => 'Aquí podeu afegir contingut que sinserirà a la part final de la secció <head> de cada pàgina. És útil per a sobreescriure estils o afegir-hi codi danalítiques.',
'app_custom_html_disabled_notice' => 'El contingut personalitzat a la capçalera HTML es desactiva en aquesta pàgina de la configuració per a assegurar que qualsevol canvi que trenqui el web es pugui desfer.',
'app_logo' => 'Logo de laplicació',
'app_logo_desc' => 'Es fa servir a la barra de la capçalera de laplicació, a banda daltres zones. Aquesta imatge ha de fer 86 px dalçada. Les imatges massa grosses es reduiran.',
'app_custom_html_desc' => 'El contingut que safegeixi aquí sinserirà al final de la secció <head> de cada pàgina. Això permet sobreescriure els estils o afegir codi danalítiques web.',
'app_custom_html_disabled_notice' => 'El contingut personalitzat a la capçalera HTML es desactivat en aquesta pàgina perquè es puguin revertir els canvis que trenquin el lloc web.',
'app_logo' => 'Logotip de laplicació',
'app_logo_desc' => 'El logotip sutilitzarà a la barra de la capçalera de laplicació, entre daltres llocs. La imatge ha de tenir 86px dalçada. Les imatges més grans es reduiran.',
'app_icon' => 'Icona de laplicació',
'app_icon_desc' => 'Aquesta icona es fa servir a la pestanya del navegador i a les icones de les dreceres. Hauria de ser una imatge PNG quadrada de 256 px.',
'app_icon_desc' => 'La icona sutilitzarà a les pestanyes del navegador i a les icones de les adreces. La imatge ha de ser un PNG quadrat de 256px de costat.',
'app_homepage' => 'Pàgina dinici de laplicació',
'app_homepage_desc' => 'Seleccioneu la visualització que es mostrarà a la pàgina dinici en lloc de la visualització per defecte. Els permisos de pàgines signoraran per a les pàgines seleccionades.',
'app_homepage_select' => 'Selecciona una pàgina',
'app_footer_links' => 'Enllaços al peu de pàgina',
'app_footer_links_desc' => 'Afegiu enllaços que es mostraran al peu de pàgina del lloc. Es mostraran a la part inferior de la majoria de pàgines, incloent-hi les que no requereixen iniciar la sessió. Podeu utilitzar letiqueta «trans::<clau>» per a fer servir traduccions definides pel sistema. Per exemple, si feu servir «trans::common.privacy_policy», es mostrarà el text traduït «Política de privadesa», i amb «trans::common.terms_of_service» es mostrarà el text traduït «Condicions del servei».',
'app_footer_links_label' => 'Etiqueta de lenllaç',
'app_homepage_desc' => 'Seleccioneu una vista per a mostrar a la pàgina dinici en comptes de la vista per defecte. Els permisos de pàgina signoren a les pàgines seleccionades.',
'app_homepage_select' => 'Seleccioneu una pàgina',
'app_footer_links' => 'Enllaços del peu de pàgina',
'app_footer_links_desc' => 'Afegiu enllaços per a mostrar al peu de pàgina. Els enllaços es mostraran al final de la majoria de les pàgines, incloent-hi les pàgines per a les que no es requereix iniciar sessió. Podeu utilitzar una etiqueta &laquo;trans::<key>&raquo; per a utilitzar traduccions definides pel sistema. Per exemple: Si utilitzeu &laquo;trans::common.privacy_policy&raquo; es mostrarà la traducció de &laquo;Privacy Policy&raquo; i si utilitzeu &laquo;trans::common.terms_of_service&raquo; es mostrarà la traducció de &laquo;Terms of Service&raquo;.',
'app_footer_links_label' => 'Text de lenllaç',
'app_footer_links_url' => 'URL de lenllaç',
'app_footer_links_add' => 'Afegeix un enllaç al peu de pàgina',
'app_footer_links_add' => 'Afegeix lenllaç en el peu de pàgina',
'app_disable_comments' => 'Desactiva els comentaris',
'app_disable_comments_toggle' => 'Desactiva els comentaris',
'app_disable_comments_desc' => 'Desactiva els comentaris a totes les pàgines de laplicació. <br> Els comentaris existents no es mostraran.',
'app_disable_comments_desc' => 'Desactiva els comentaris a totes les pàgines de laplicació. <br> Els comentaris existents no es mostraran.',
// Color settings
'color_scheme' => 'Esquema de colors de laplicació',
'color_scheme_desc' => 'Definiu els colors que sutilitzaran a la interfície dusuari de laplicació. Els colors es poden configurar de manera separada per als modes fosc i clar perquè encaixin millor amb el tema i nassegurin la llegibilitat.',
'ui_colors_desc' => 'Definiu el color primari de laplicació i el color per defecte dels enllaços. El color primari es fa servir sobretot per a la capçalera, els botons i la decoració de la interfície. El color per defecte dels enllaços sutilitza per als enllaços de text i les accions, tant al contingut escrit com a la interfície de laplicació.',
'app_color' => 'Color primari',
'color_scheme_desc' => 'Configureu els colors que sutilitzaran a la interfície dusuari de laplicació. Podeu configurar els colors pel mode clar o el mode fosc per separat perquè escaigui millor al tema i assegurar la llegibilitat.',
'ui_colors_desc' => 'Configureu el color principal de laplicació i el color per defecte dels enllaços. El color principal sutilitza al bàner de la capçalera, els botons i les decoracions de la interfície. El color per defecte dels enllaços sutilitza a les accions i els enllaços de text, tant al contingut escrit com a la interfície de laplicació.',
'app_color' => 'Color principal',
'link_color' => 'Color per defecte dels enllaços',
'content_colors_desc' => 'Definiu els colors per a tots els elements de la jerarquia dorganització de pàgines. És recomanable triar colors amb una brillantor similar a la dels colors per defecte per a millorar la llegibilitat.',
'content_colors_desc' => 'Configureu els colors per a tots els elements en la jerarquia de lorganització de la pàgina. És recomanable que trieu uns colors amb una brillantor similar a la dels colors per defecte per assegurar la llegibilitat.',
'bookshelf_color' => 'Color dels prestatges',
'book_color' => 'Color dels llibres',
'chapter_color' => 'Color dels capítols',
'page_color' => 'Color de les pàgines',
'page_draft_color' => 'Color dels esborranys de pàgines',
'page_draft_color' => 'Color de les pàgines desborrany',
// Registration Settings
'reg_settings' => 'Registre',
'reg_enable' => 'Activa el registre dusuaris',
'reg_enable_toggle' => 'Activa el registre dusuaris',
'reg_enable_desc' => 'Si els registres estan activats, els usuaris podran registrar-se ells mateixos com a usuaris de laplicació. Un cop registrats, sels assigna un únic rol dusuari per defecte.',
'reg_default_role' => 'Rol dusuari per defecte en registrar-se',
'reg_enable_external_warning' => 'Lopció anterior signora quan hi ha activada lautenticació SAML o LDAP externa. Els comptes dusuari de membres inexistents es crearan automàticament si lautenticació contra el sistema extern és satisfactòria.',
'reg_email_confirmation' => 'Confirmació de correu electrònic',
'reg_email_confirmation_toggle' => 'Requereix la confirmació per correu electrònic',
'reg_confirm_email_desc' => 'Si sutilitza la restricció de dominis, serà obligatòria la confirmació per correu electrònic, i signorarà aquesta opció.',
'reg_enable_desc' => 'Quan el registre està activat, els usuaris es podran registrar com a usuari de laplicació. Un cop registrat, sels assigna un rol dusuari per defecte únic.',
'reg_default_role' => 'Rol dusuari per defecte després del registre.',
'reg_enable_external_warning' => 'Signorarà lopció de sobre quan lautenticació LDAP or SAML estigui activada. Es crearan automàticament comptes dusuari per als membres que encara no ho siguin si no és possible lautenticació amb els sistema dautenticació extern.',
'reg_email_confirmation' => 'Correu electrònic de confirmació',
'reg_email_confirmation_toggle' => 'Requereix un correu electrònic de confirmació',
'reg_confirm_email_desc' => 'Si sutilitza la restricció de dominis es requerirà un correu electrònic de confirmació i signorarà aquesta opció.',
'reg_confirm_restrict_domain' => 'Restricció de dominis',
'reg_confirm_restrict_domain_desc' => 'Introduïu una llista separada per comes de dominis de correu electrònic als quals voleu restringir els registres. Senviarà un correu electrònic als usuaris perquè confirmin la seva adreça abans de permetrels interactuar amb laplicació. <br> Tingueu en compte que els usuaris podran canviar les seves adreces electròniques després de registrar-se correctament.',
'reg_confirm_restrict_domain_desc' => 'Introduïu una llista separada per comes dels dominis a què voleu restringir el registre. Senviarà un correu electrònic als usuaris perquè confirmin la seva adreça electrònica abans que puguin interactuar amb laplicació. <br> Tingueu en compte que els usuaris podran canviar ladreça electrònica un cop shagin registrat.',
'reg_confirm_restrict_domain_placeholder' => 'No hi ha cap restricció',
// Maintenance settings
'maint' => 'Manteniment',
'maint_image_cleanup' => 'Neteja les imatges',
'maint_image_cleanup_desc' => 'Escaneja el contingut de les pàgines i les revisions per a comprovar quines imatges i diagrames estan en ús actualment i quines imatges són redundants. Assegureu-vos de crear una còpia de seguretat completa de la base de dades i de les imatges abans dexecutar això.',
'maint_delete_images_only_in_revisions' => 'Suprimeix també les imatges que només existeixin en revisions antigues de pàgines',
'maint_image_cleanup' => 'Neteja dimatges',
'maint_image_cleanup_desc' => 'Escanegeu les pàgines i les revisions per a comprovar quines imatges o dibuixos sutilitzen i quins no. Assegureu-vos de crear una còpia de seguretat completa de la base de dades i de les imatges abans dexecutar-la.',
'maint_delete_images_only_in_revisions' => 'Suprimiu també les imatges que només existeixen en revisions de pàgina antigues.',
'maint_image_cleanup_run' => 'Executa la neteja',
'maint_image_cleanup_warning' => 'Shan trobat :count imatges potencialment no utilitzades. Segur que voleu suprimir aquestes imatges?',
'maint_image_cleanup_success' => 'Shan trobat i suprimit :count imatges potencialment no utilitzades!',
'maint_image_cleanup_nothing_found' => 'No sha trobat cap imatge no utilitzada, així que no sha suprimit res!',
'maint_send_test_email' => 'Envia un correu electrònic de prova',
'maint_send_test_email_desc' => 'Envia un correu electrònic de prova a ladreça electrònica que hàgiu especificat al perfil.',
'maint_image_cleanup_warning' => 'Imatges que potencialment no sutilitzen: :count. Esteu segur que voleu suprimir aquestes imatges?',
'maint_image_cleanup_success' => 'Imatges que potencialment no sutilitzen que shan suprimit: :count.',
'maint_image_cleanup_nothing_found' => 'No sha trobat cap imatge que no sutilitzi. No sha suprimit res.',
'maint_send_test_email' => 'Envia un correu electrònic de prova.',
'maint_send_test_email_desc' => 'Senvia un correu electrònic de prova a ladreça electrònica que figura al vostre perfil.',
'maint_send_test_email_run' => 'Envia el correu electrònic de prova',
'maint_send_test_email_success' => 'Sha enviat el correu electrònic a :address',
'maint_send_test_email_success' => 'Sha enviat un correu electrònic a :address',
'maint_send_test_email_mail_subject' => 'Correu electrònic de prova',
'maint_send_test_email_mail_greeting' => 'El lliurament de correus electrònics sembla que funciona!',
'maint_send_test_email_mail_text' => 'Enhorabona! Com que heu rebut aquesta notificació per correu electrònic, la vostra configuració del correu electrònic sembla que està ben configurada.',
'maint_recycle_bin_desc' => 'Els prestatges, llibres, capítols i pàgines suprimits senvien a la paperera de reciclatge perquè es puguin restaurar o suprimir de manera permanent. Pot ser que els elements més antics de la paperera de reciclatge se suprimeixin automàticament després dun temps, depenent de la configuració del sistema.',
'maint_recycle_bin_open' => 'Obre la paperera de reciclatge',
'maint_send_test_email_mail_greeting' => 'Sembla que lenviament de correus electrònics funciona.',
'maint_send_test_email_mail_text' => 'Enhorabona! Que hagis rebut aquesta notificació de correu electrònic vol dir que la vostra configuració de correu electrònic és correcta.',
'maint_recycle_bin_desc' => 'Els prestatges, els llibres, els capítols i les pàgines suprimides senvien a la paperera perquè es puguin restaurar o suprimir permanentment. És possible que els elements que fa més temps que són a la paperera seliminin automàticament al cap dun temps segons la configuració del sistema.',
'maint_recycle_bin_open' => 'Obre la paperera',
'maint_regen_references' => 'Regenera les referències',
'maint_regen_references_desc' => 'Aquesta acció reconstruirà líndex de referències entre elements de la base de dades. Normalment es gestiona automàticament, però aquesta acció pot ser útil per a indexar contingut antic o contingut afegit mitjançant mètodes no oficials.',
'maint_regen_references_success' => 'Líndex de referències sha regenerat!',
'maint_timeout_command_note' => 'Nota: Aquesta acció pot trigar estona a executar-se, la qual cosa pot provocar errors de temps dexpera excedits en alguns entorns web. Com a alternativa, podeu executar aquesta acció en una ordre al terminal.',
'maint_regen_references_desc' => 'Aquesta acció reconstruirà líndex de referències creuades entre elements a la base de dades. Normalment es fa automàticament però aquesta acció és útil per a indexar contingut antic o contingut afegit a través de mètodes no oficials.',
'maint_regen_references_success' => 'Sha regenerat líndex de referències.',
'maint_timeout_command_note' => 'Nota: És possible que aquesta acció trigui a executar-se cosa que pot provocar que sexcedeixi el temps despera en alguns entorns web. Com a alternativa, podeu executar aquesta acció amb una ordre del terminal.',
// Recycle Bin
'recycle_bin' => 'Paperera de reciclatge',
'recycle_bin_desc' => 'Aquí podeu restaurar els elements que hàgiu suprimit o triar suprimir-los del sistema de manera permanent. Aquesta llista no té cap filtre, al contrari que altres llistes dactivitat similars en què es tenen en compte els filtres de permisos.',
'recycle_bin' => 'Paperera',
'recycle_bin_desc' => 'Aquí podeu restaurar els elements que shan suprimit o suprimir-los permanentment del sistema. Aquesta llista no està filtrada, a diferència de llistes dactivitat similars on sapliquen filtres de permisos.',
'recycle_bin_deleted_item' => 'Element suprimit',
'recycle_bin_deleted_parent' => 'Pare',
'recycle_bin_deleted_by' => 'Suprimit per',
'recycle_bin_deleted_at' => 'Moment de la supressió',
'recycle_bin_permanently_delete' => 'Suprimeix permanentment',
'recycle_bin_deleted_at' => 'Hora de supressió',
'recycle_bin_permanently_delete' => 'Suprimit permanentment',
'recycle_bin_restore' => 'Restaura',
'recycle_bin_contents_empty' => 'La paperera de reciclatge és buida',
'recycle_bin_empty' => 'Buida la paperera de reciclatge',
'recycle_bin_empty_confirm' => 'Se suprimiran de manera permanent tots els elements de la paperera de reciclatge, incloent-hi el contingut dins de cada element. Segur que voleu buidar la paperera de reciclatge?',
'recycle_bin_contents_empty' => 'La paperera és buida',
'recycle_bin_empty' => 'Buida la paperera',
'recycle_bin_empty_confirm' => 'Se suprimiran permanentment tots els elements que hi ha a la paperera incloent-hi el contingut que hi hagi a cada element. Esteu segur que voleu buidar la paperera?',
'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
'recycle_bin_destroy_list' => 'Elements que es destruiran',
'recycle_bin_restore_list' => 'Elements que es restauraran',
'recycle_bin_restore_confirm' => 'Aquesta acció restaurarà lelement suprimit, incloent-hi tots els elements fills, a la seva ubicació original. Si la ubicació original ha estat suprimida, i ara és a la paperera de reciclatge, caldrà que també en restaureu lelement pare.',
'recycle_bin_restore_deleted_parent' => 'El pare daquest element també ha estat suprimit. Lelement es mantindrà suprimit fins que el pare també es restauri.',
'recycle_bin_restore_parent' => 'Restauran el pare',
'recycle_bin_destroy_notification' => 'Shan suprimit :count elements en total de la paperera de reciclatge.',
'recycle_bin_restore_notification' => 'Shan restaurat :count elements en total de la paperera de reciclatge.',
'recycle_bin_destroy_list' => 'Elements per destruir',
'recycle_bin_restore_list' => 'Elements per restaurar',
'recycle_bin_restore_confirm' => 'Aquesta acció restaurarà lelement suprimit, incloent-hi els elements fills, a la seva ubicació original. Si la ubicació original sha suprimit i és a la paperera, lelement pare també shaurà de restaurar.',
'recycle_bin_restore_deleted_parent' => 'El pare daquest element també sha suprimit. Lelement continuarà suprimit fins que no sen restauri també el pare.',
'recycle_bin_restore_parent' => 'Restaura el pare',
'recycle_bin_destroy_notification' => 'Elements suprimits de la paperera: :count.',
'recycle_bin_restore_notification' => 'Elements restaurats de la paperera: :count.',
// Audit Log
'audit' => 'Registre dauditoria',
'audit_desc' => 'Aquest registre dauditoria mostra una llista dactivitats registrades al sistema. Aquesta llista no té cap filtre, al contrari que altres llistes dactivitat similars en què es tenen en compte els filtres de permisos.',
'audit_desc' => 'El registre d\'auditoria mostra una llista de les activitats de què es fa un seguiment. Aquesta llista no està filtrada, a diferència de llistes dactivitat similars on sapliquen filtres de permisos.',
'audit_event_filter' => 'Filtre desdeveniments',
'audit_event_filter_no_filter' => 'Sense filtre',
'audit_deleted_item' => 'Element suprimit',
@@ -130,150 +130,150 @@ return [
'audit_table_related' => 'Element relacionat o detall',
'audit_table_ip' => 'Adreça IP',
'audit_table_date' => 'Data de lactivitat',
'audit_date_from' => 'Rang de dates a partir de',
'audit_date_to' => 'Rang de rates fins a',
'audit_date_from' => 'Des de',
'audit_date_to' => 'Fins a',
// Role Settings
'roles' => 'Rols',
'role_user_roles' => 'Rols dusuari',
'roles_index_desc' => 'Els rols sutilitzen per a agrupar usuaris i proporcionar permisos del sistema a llurs membres. Quan un usuari és membre de múltiples rols, els privilegis que li concedeixin sacumularan i lusuari heretarà totes les habilitats.',
'roles_index_desc' => 'Els rols dusuari sutilitzen per a agrupar usuaris i donar-los permisos conjuntament. Un usuari que tingui més dun rol acumularà els privilegis que satorguin a tots els rols i nheretarà els permisos.',
'roles_x_users_assigned' => ':count usuari assignat|:count usuaris assignats',
'roles_x_permissions_provided' => ':count permís|:count permisos',
'roles_assigned_users' => 'Usuaris assignats',
'roles_permissions_provided' => 'Permisos proporcionats',
'role_create' => 'Crea un rol nou',
'role_delete' => 'Suprimeix el rol',
'role_delete_confirm' => 'Se suprimirà el rol amb el nom «:roleName».',
'role_delete_users_assigned' => 'Aquest rol :userCount usuaris assignats. Si voleu migrar els usuaris daquest rol, seleccioneu un rol nou a continuació.',
'roles_permissions_provided' => 'Permisos atorgats',
'role_create' => 'Crea un rol',
'role_delete' => 'Suprimeix un rol',
'role_delete_confirm' => 'Se suprimirà el rol &laquo;:roleName&raquo;.',
'role_delete_users_assigned' => 'Usuaris assignats en aquest rol: :userCount. Si voleu migrar aquests usuaris a un altre rol, seleccioneu-ne un dels de sota.',
'role_delete_no_migration' => "No migris els usuaris",
'role_delete_sure' => 'Segur que voleu suprimir aquest rol?',
'role_delete_sure' => 'Esteu segur que voleu suprimir aquest rol?',
'role_edit' => 'Edita el rol',
'role_details' => 'Detalls del rol',
'role_name' => 'Nom del rol',
'role_desc' => 'Descripció curta del rol',
'role_mfa_enforced' => 'Requereix autenticació de múltiple factor',
'role_desc' => 'Descripció del rol',
'role_mfa_enforced' => 'Autenticació multifactorial requerida',
'role_external_auth_id' => 'Identificadors dautenticació externa',
'role_system' => 'Permisos del sistema',
'role_manage_users' => 'Gestiona usuaris',
'role_manage_roles' => 'Gestiona rols i permisos de rols',
'role_manage_entity_permissions' => 'Gestiona els permisos de tots els llibres, capítols i pàgines',
'role_manage_own_entity_permissions' => 'Gestiona els permisos dels llibres, capítols i pàgines propis',
'role_manage_page_templates' => 'Gestiona les plantilles de pàgines',
'role_access_api' => 'Accedeix a lAPI del sistema',
'role_manage_settings' => 'Gestiona la configuració de laplicació',
'role_export_content' => 'Exporta el contingut',
'role_editor_change' => 'Canvia leditor de pàgines',
'role_notifications' => 'Rep i gestiona les notificacions',
'role_manage_users' => 'Gestió dels usuaris',
'role_manage_roles' => 'Gestió dels rols i els seus permisos',
'role_manage_entity_permissions' => 'Gestió de tots els permisos de llibres, capítols i pàgines',
'role_manage_own_entity_permissions' => 'Gestió dels permisos als seus llibres, capítols i pàgines',
'role_manage_page_templates' => 'Gestió de les plantilles de pàgina',
'role_access_api' => 'Accés a lAPI del sistema',
'role_manage_settings' => 'Gestió de la configuració de laplicació',
'role_export_content' => 'Exportació de contingut',
'role_editor_change' => 'Canvi de leditor de pàgina',
'role_notifications' => 'Recepció i gestió de notificacions',
'role_asset' => 'Permisos de recursos',
'roles_system_warning' => 'Tingueu en compte que laccés a qualsevol dels tres permisos de dalt pot permetre que un usuari alteri els seus propis permisos o els privilegis daltres usuaris del sistema. Assigneu rols amb aquests permisos només a usuaris de confiança.',
'role_asset_desc' => 'Aquests permisos controlen laccés per defecte als recursos del sistema. Els permisos de llibres, capítols i pàgines tindran més importància que aquests permisos.',
'role_asset_admins' => 'Els administradors tenen accés automàticament a tot el contingut, però aquestes opcions poden mostrar o amagar opcions de la interfície dusuari.',
'role_asset_image_view_note' => 'Això és relatiu a la visibilitat dins del gestor dimatges. Laccés real als fitxers dimatge pujats dependrà de lopció demmagatzematge dimatges del sistema.',
'roles_system_warning' => 'Tingueu en compte que laccés a qualsevol dels tres permisos de dalt permeten que lusuari canviï els seus privilegis i els privilegis daltres usuaris. Assigneu rols dusuari amb aquests permisos només a usuaris de confiança.',
'role_asset_desc' => 'Aquests permisos controlen laccés per defecte als recursos del sistema. El permisos dels llibres, capítols i pàgines sobreescriuran aquests permisos.',
'role_asset_admins' => 'Als administradors sels dona accés automàticament a tot el contingut però aquestes opcions mostren o amaguen opcions de la interfície dusuari.',
'role_asset_image_view_note' => 'Això té relació amb la visibilitat al gestor dimatges. Laccés a les imatges pujades dependrà de lopció demmagatzematge dimatges dels sistema.',
'role_all' => 'Tot',
'role_own' => 'Propi',
'role_controlled_by_asset' => 'Controlat pel recurs en què es pugen',
'role_controlled_by_asset' => 'Controlat pel recurs a què estan pujats',
'role_save' => 'Desa el rol',
'role_users' => 'Usuaris amb aquest rol',
'role_users_none' => 'Ara mateix no hi ha cap usuari assignat a aquest rol',
'role_users' => 'Usuaris assignats en aquest rol',
'role_users_none' => 'No hi ha cap usuari assignat en aquest rol',
// Users
'users' => 'Usuaris',
'users_index_desc' => 'Creeu i gestioneu comptes dusuari individuals dins del sistema. Els comptes dusuari sutilitzen per a iniciar la sessió i atribuir el contingut i les activitats. Els permisos daccés són principalment basats en rols, però si un usuari és propietari o no dun contingut, entre altres factors, també pot afectar-ne els permisos i laccés.',
'user_profile' => 'Perfil de lusuari',
'users_index_desc' => 'Creeu i gestioneu comptes dusuari individuals. Els comptes dusuari sutilitzen per als inicis de sessió i les atribucions de contingut i activitat. Els permisos daccés es basen fonamentalment en el rol dusuari però la propietat del contingut, entre daltres, també afecta els permisos i laccés.',
'user_profile' => 'Perfil dusuari',
'users_add_new' => 'Afegeix un usuari nou',
'users_search' => 'Cerca usuaris',
'users_latest_activity' => 'Darrera activitat',
'users_latest_activity' => 'Activitat més recent',
'users_details' => 'Detalls de lusuari',
'users_details_desc' => 'Definiu un nom públic i una adreça electrònica per a aquest usuari. Ladreça electrònica es farà servir per a iniciar la sessió a laplicació.',
'users_details_desc_no_email' => 'Definiu un nom públic per a aquest usuari perquè els altres el puguin reconèixer.',
'users_role' => 'Rols de lusuari',
'users_role_desc' => 'Seleccioneu a quins rols sassignarà lusuari. Si un usuari sassigna a múltiples rols, els permisos dels rols sacumularan i lusuari rebrà totes les habilitats dels rols assignats.',
'users_password' => 'Contrasenya de lusuari',
'users_password_desc' => 'Definiu una contrasenya que sutilitzarà per a iniciar la sessió a laplicació. Cal que tingui un mínim de 8 caràcters.',
'users_send_invite_text' => 'Podeu elegir enviar un correu dinvitació a aquest usuari, la qual cosa li permetrà definir la seva contrasenya, o podeu definir-li una contrasenya vós.',
'users_send_invite_option' => 'Envia un correu dinvitació a lusuari',
'users_details_desc' => 'Configureu un nom i una adreça electrònica per a aquest usuari. Ladreça electrònica susarà per a iniciar sessió a laplicació.',
'users_details_desc_no_email' => 'Configureu un nom per a aquest usuari perquè sel pugui reconèixer.',
'users_role' => 'Rols dusuari',
'users_role_desc' => 'Seleccioneu quins rols sassignaran en aquest usuari. Si a un usuari se li assignen més dun rol acumularà els privilegis que satorguin a tots els rols i nheretarà les els permisos.',
'users_password' => 'Contrasenya dusuari',
'users_password_desc' => 'Configureu la contrasenya que sutilitzarà per a iniciar sessió a laplicació. Ha de tenir com a mínim 8 caràcters.',
'users_send_invite_text' => 'Podeu configurar la contrasenya o enviar un correu electrònic que convidi a lusuari a configurar-la.',
'users_send_invite_option' => 'Envia el correu electrònic',
'users_external_auth_id' => 'Identificador dautenticació extern',
'users_external_auth_id_desc' => 'Quan es fa servir un sistema dautenticació extern (com ara SAML2, OIDC o LDAP), aquest és lidentificador que enllaça aquest usuari del BookStack amb el compte del sistema dautenticació. Podeu ignorar aquest camp si feu servir lautenticació basada en correu electrònic per defecte.',
'users_password_warning' => 'Ompliu-ho només si voleu canviar la contrasenya daquest usuari.',
'users_system_public' => 'Aquest usuari representa qualsevol usuari convidat que visita la vostra instància. No es pot fer servir per a iniciar la sessió, però sassigna automàticament.',
'users_external_auth_id_desc' => 'Quan sutilitza un sistema dautenticació extern (com ara SAML2, OIDC or LDAP) lidentificador enllaça lusuari amb el compte dels sistema dautenticació. Podeu ignorar aquest camp si utilitzeu lautenticació amb correu electrònic per defecte.',
'users_password_warning' => 'Ompliu els camps de sota només si voleu canviar la contrasenya daquest usuari.',
'users_system_public' => 'Aquest usuari representa qualsevol usuari convidat que visiti la instància. No es pot utilitzar per a iniciar sessió sinó que sassigna automàticament.',
'users_delete' => 'Suprimeix lusuari',
'users_delete_named' => 'Suprimeix lusuari :userName',
'users_delete_warning' => 'Se suprimirà completament del sistema lusuari amb el nom «:userName».',
'users_delete_confirm' => 'Segur que voleu suprimir aquest usuari?',
'users_migrate_ownership' => 'Migra lautoria',
'users_migrate_ownership_desc' => 'Seleccioneu un usuari si voleu que un altre usuari esdevingui el propietari de tots els elements que ara són propietat daquest usuari.',
'users_none_selected' => 'No hi ha cap usuari seleccionat',
'users_delete_warning' => 'Se suprimirà lusuari &laquo;:userName&raquo; del sistema.',
'users_delete_confirm' => 'Esteu segur que voleu suprimir aquest usuari?',
'users_migrate_ownership' => 'Migració de la propietat',
'users_migrate_ownership_desc' => 'Si voleu que un altre usuari esdevingui el propietari de tots els elements daquest usuari, seleccioneu-ne un.',
'users_none_selected' => 'No sha seleccionat cap usuari',
'users_edit' => 'Edita lusuari',
'users_edit_profile' => 'Edita el perfil',
'users_avatar' => 'Avatar de lusuari',
'users_avatar_desc' => 'Seleccioneu una imatge que representi aquest usuari. Hauria de ser un quadrat daproximadament 256 px.',
'users_preferred_language' => 'Llengua preferida',
'users_preferred_language_desc' => 'Aquesta opció canvia la llengua utilitzada a la interfície dusuari de laplicació. No afectarà el contingut creat pels usuaris.',
'users_social_accounts' => 'Comptes socials',
'users_social_accounts_desc' => 'Vegeu lestat dels comptes socials connectats daquest usuari. Els comptes socials es poden fer servir per a accedir al sistema de manera addicional al sistema dautenticació principal.',
'users_social_accounts_info' => 'Aquí podeu connectar altres comptes per a un inici de sessió més ràpid i còmode. Si desconnecteu un compte aquí, no en revoqueu laccés dautorització donat amb anterioritat. Revoqueu-hi laccés a la configuració del perfil del compte social que hàgiu connectat.',
'users_social_connect' => 'Connecta un compte',
'users_avatar' => 'Avatar',
'users_avatar_desc' => 'Seleccioneu una imatge per a representar aquest usuari. Ha de ser una imatge quadrada de 256px de costat, aproximadament.',
'users_preferred_language' => 'Llengua',
'users_preferred_language_desc' => 'Canvia la llengua en què es mostra la interfície dusuari de laplicació. No afectarà el contingut creat pels usuaris.',
'users_social_accounts' => 'Comptes a les xarxes socials',
'users_social_accounts_desc' => 'Mireu lestat dels comptes socials connectats daquest usuari. També es poden utilitzar els comptes socials per a iniciar sessió a banda del sistema dautenticació principal.',
'users_social_accounts_info' => 'Connecteu els vostres comptes socials per a iniciar sessió més ràpidament. La desconnexió dun compte no revoca lautorització daccés atorgada prèviament. Revoqueu laccés des de la configuració de perfil del compte social connectat.',
'users_social_connect' => 'Connecta el compte',
'users_social_disconnect' => 'Desconnecta el compte',
'users_social_status_connected' => 'Connectat',
'users_social_status_disconnected' => 'Desconnectat',
'users_social_connected' => 'El compte de :socialAccount sha associat correctament al vostre perfil.',
'users_social_disconnected' => 'El compte de :socialAccount ha desassociat correctament del vostre perfil.',
'users_api_tokens' => 'Testimonis de lAPI',
'users_api_tokens_desc' => 'Creeu i gestioneu els testimonis daccés que sutilitzen per a autenticar-vos a lAPI REST del BookStack. Els permisos de lAPI es gestionen fent servir lusuari al qual pertany el testimoni.',
'users_api_tokens_none' => 'No sha creat cap testimoni de lAPI per a aquest usuari',
'users_social_connected' => 'Sha connectat el compte :socialAccount al vostre perfil.',
'users_social_disconnected' => 'Sha desconnectat el compte :socialAccount del vostre perfil.',
'users_api_tokens' => 'Testimonis API',
'users_api_tokens_desc' => 'Creeu i gestioneu els testimonis daccés que sutilitzen per autenticar els usuaris amb la lAPI REST del BookStack. Els permisos de lAPI es gestionen a través de lusuari a qui pertany el testimoni.',
'users_api_tokens_none' => 'No sha creat cap testimoni API per a aquest usuari',
'users_api_tokens_create' => 'Crea un testimoni',
'users_api_tokens_expires' => 'Caducitat',
'users_api_tokens_docs' => 'Documentació de lAPI',
'users_mfa' => 'Autenticació de múltiple factor',
'users_mfa_desc' => 'Configureu lautenticació de múltiple factor com a capa extra de seguretat en el vostre compte dusuari.',
'users_mfa_x_methods' => ':count mètode configurat|:count mètodes configurats',
'users_mfa_configure' => 'Configura els mètodes',
'users_mfa' => 'Autenticació multifactorial',
'users_mfa_desc' => 'Configureu lautenticació multifactorial per a afegir una capa de seguretat extra al vostre compte dusuari.',
'users_mfa_x_methods' => 'Hi ha :count mètode configurat|Hi ha :count mètodes configurats',
'users_mfa_configure' => 'Configura un mètode',
// API Tokens
'user_api_token_create' => 'Crea un testimoni de lAPI',
'user_api_token_create' => 'Crea un testimoni API',
'user_api_token_name' => 'Nom',
'user_api_token_name_desc' => 'Poseu un nom llegible al vostre testimoni com a recordatori futur del propòsit al qual el voleu destinar.',
'user_api_token_name_desc' => 'Anomeneu el testimoni amb un nom entenedor que permeti saber-ne el propòsit.',
'user_api_token_expiry' => 'Data de caducitat',
'user_api_token_expiry_desc' => 'Definiu una data en què aquest testimoni caducarà. Després daquesta data, les peticions fetes amb aquest testimoni deixaran de funcionar. Si deixeu aquest camp en blanc, es definirà una caducitat daquí a 100 anys.',
'user_api_token_create_secret_message' => 'Just després de crear aquest testimoni, es generaran i es mostraran un «Identificador del testimoni» i un «Secret del testimoni». El secret només es mostrarà una única vegada: assegureu-vos de copiar-lo a un lloc segur abans de continuar.',
'user_api_token' => 'Testimoni de lAPI',
'user_api_token_id' => 'Identificador del testimoni',
'user_api_token_id_desc' => 'Aquest identificador és generat pel sistema per a aquest testimoni i no és editable i caldrà que el proporcioneu a les peticions a lAPI.',
'user_api_token_secret' => 'Secret del testimoni',
'user_api_token_secret_desc' => 'Aquest secret és generat pel sistema per a aquest testimoni i caldrà que el proporcioneu a les peticions a lAPI. Només es mostrarà aquesta única vegada, assegureu-vos de copiar-lo a un lloc segur.',
'user_api_token_expiry_desc' => 'Configureu la data de caducitat del testimoni. Un cop passada aquesta data, les sol·licituds fetes amb aquest testimoni no funcionaran. Si deixeu aquest camp en blanc, el testimoni caducarà daquí 100 anys.',
'user_api_token_create_secret_message' => 'Es crearà i es mostrarà un &laquo;identificador de testimoni&raquo; i un &laquo;secret de testimoni&raquo; immediatament després de crear aquest testimoni. El secret es mostrarà només un sol cop. Assegureu-vos danotar-lo i de desar-lo en un lloc segur abans de continuar.',
'user_api_token' => 'Testimoni API',
'user_api_token_id' => 'Identificador de testimoni',
'user_api_token_id_desc' => 'És un identificador no editable generat pel sistema per a aquest testimoni i que shaurà de proporcionar en les sol·licituds API.',
'user_api_token_secret' => 'Secret de testimoni',
'user_api_token_secret_desc' => 'És un secret generat pel sistema per a aquest testimoni i que shaurà de proporcionar en les sol·licituds API. Es mostrarà només un sol cop. Assegureu-vos danotar-lo i de desar-lo en un lloc segur abans de continuar.',
'user_api_token_created' => 'Testimoni creat :timeAgo',
'user_api_token_updated' => 'Testimoni actualitzat :timeAgo',
'user_api_token_delete' => 'Suprimeix el testimoni',
'user_api_token_delete_warning' => 'Se suprimirà completament del sistema aquest testimoni de lAPI amb el nom «:tokenName».',
'user_api_token_delete_confirm' => 'Segur que voleu suprimir aquest testimoni de lAPI?',
'user_api_token_delete_warning' => 'Se suprimirà el testimoni API &laquo;:tokenName&raquo; del sistema.',
'user_api_token_delete_confirm' => 'Esteu segur que voleu suprimir aquest testimoni API?',
// Webhooks
'webhooks' => 'Webhooks',
'webhooks_index_desc' => 'Els webhooks són una manera denviar dades a URL externs quan es produeixen certes accions i esdeveniments al sistema, la qual cosa permet una integració basada en esdeveniments amb plataformes externs, com ara sistemes de missatgeria o de notificacions.',
'webhooks_x_trigger_events' => ':count esdeveniment disparador|:count elements disparadors',
'webhooks_create' => 'Crea un webhook nou',
'webhooks_none_created' => 'Encara no sha creat cap webhook.',
'webhooks_index_desc' => 'Els webhooks permeten enviar dades a URLs externs quan ocorren unes accions o esdeveniments determinats. Això permet la integració basada en esdeveniments amb plataformes externes com ara sistemes de missatgeria o notificació.',
'webhooks_x_trigger_events' => 'Hi ha :count esdeveniment disparador|Hi ha :count esdeveniments disparadors',
'webhooks_create' => 'Crea un webhook',
'webhooks_none_created' => 'No hi ha cap webhook',
'webhooks_edit' => 'Edita el webhook',
'webhooks_save' => 'Desa el webhook',
'webhooks_details' => 'Detalls del webhook',
'webhooks_details_desc' => 'Proporcioneu un nom amigable i un endpoint POST com a ubicació on senviaran les dades del webhook.',
'webhooks_events' => 'Esdeveniments del webhook',
'webhooks_events_desc' => 'Seleccioneu tots els esdeveniments que haurien de fer que es cridés aquest webhook.',
'webhooks_events_warning' => 'Tingueu en compte que aquests esdeveniments es produiran per a tots els esdeveniments seleccionats, fins i tot si shi apliquen permisos personalitzats. Assegureu-vos que aquest webhook no exposarà contingut confidencial.',
'webhooks_details_desc' => 'Anomeneu el webhook amb un nom entenedor i proporcioneu un extrem POST com a ubicació on enviar les dades per al webhook.',
'webhooks_events' => 'Esdeveniments webhook',
'webhooks_events_desc' => 'Seleccioneu els esdeveniment que voleu que disparin la crida daquest webhook.',
'webhooks_events_warning' => 'Tingueu en compte que aquestes crides es dispararan a tots els esdeveniments seleccionats, inclús si sapliquen permisos personalitzats. Assegureu-vos que lús daquest webhook no exposarà contingut confidencial.',
'webhooks_events_all' => 'Tots els esdeveniments del sistema',
'webhooks_name' => 'Nom del webhook',
'webhooks_timeout' => 'Temps despera de les peticions al webhook (en segons)',
'webhooks_endpoint' => 'Endpoint del webhook',
'webhooks_timeout' => 'Temps despera del webhook (en segons)',
'webhooks_endpoint' => 'Extrem del webhook',
'webhooks_active' => 'Webhook actiu',
'webhook_events_table_header' => 'Esdeveniments',
'webhooks_delete' => 'Suprimeix el webhook',
'webhooks_delete_warning' => 'Se suprimirà completament del sistema el webhook amb el nom «:webhookName».',
'webhooks_delete_confirm' => 'Segur que voleu suprimir aquest webhook?',
'webhooks_format_example' => 'Exemple del format del webhook',
'webhooks_format_example_desc' => 'Les dades del webhook senvien com una petició POST a lendpoint configurat amb un JSON que segueix el següent format. Les propietats «related_item» i «url» són opcionals i dependran del tipus desdeveniment produït.',
'webhooks_delete_warning' => 'Se suprimirà el webhook &laquo;:webhookName&raquo; del sistema.',
'webhooks_delete_confirm' => 'Esteu segur que voleu suprimir aquest webhook?',
'webhooks_format_example' => 'Exemple de format de webhook',
'webhooks_format_example_desc' => 'Les dades dun webhook senvien com una sol·licitud POST a lextrem configurat com a JSON amb el format següent. Les propietats &laquo;related_item&raquo; i &laquo;url&raquo; són opcionals i dependran del tipus desdeveniment que es dispari.',
'webhooks_status' => 'Estat del webhook',
'webhooks_last_called' => 'Cridat per darrera vegada:',
'webhooks_last_errored' => 'Error per darrera vegada:',
'webhooks_last_called' => 'Darrera crida:',
'webhooks_last_errored' => 'Darrer error:',
'webhooks_last_error_message' => 'Darrer missatge derror:',
// Licensing
@@ -295,6 +295,7 @@ return [
'bs' => 'Bosanski',
'ca' => 'Català',
'cs' => 'Česky',
'cy' => 'Cymraeg',
'da' => 'Dansk',
'de' => 'Deutsch (Sie)',
'de_informal' => 'Deutsch (Du)',

View File

@@ -12,13 +12,13 @@ return [
'active_url' => 'El camp :attribute no és un URL vàlid.',
'after' => 'El camp :attribute ha de ser una data posterior a :date.',
'alpha' => 'El camp :attribute només pot contenir lletres.',
'alpha_dash' => 'El camp :attribute només pot contenir lletres, números, guions i guions baixos.',
'alpha_num' => 'El camp :attribute només pot contenir lletres i números.',
'array' => 'El camp :attribute ha de ser un vector.',
'backup_codes' => 'El codi que heu proporcionat no és vàlid o ja sha fet servir.',
'before' => 'El camp :attribute ha de ser una data anterior a :date.',
'alpha_dash' => 'El camp :attribute només pot contenir lletres, xifres, guionets i guions baixos.',
'alpha_num' => 'El camp :attribute només pot contenir lletres, xifres.',
'array' => 'El camp :attribute ha de ser una matriu.',
'backup_codes' => 'El codi que heu proporcionat no és vàlid o ja sha utilitzat.',
'before' => 'El camp :attribute ha de ser una data posterior a :date.',
'between' => [
'numeric' => 'El camp :attribute ha destar entre :min i :max.',
'numeric' => 'El camp :attribute ha de ser un nombre entre :min i :max.',
'file' => 'El camp :attribute ha de tenir entre :min i :max kilobytes.',
'string' => 'El camp :attribute ha de tenir entre :min i :max caràcters.',
'array' => 'El camp :attribute ha de tenir entre :min i :max elements.',
@@ -27,11 +27,11 @@ return [
'confirmed' => 'La confirmació del camp :attribute no coincideix.',
'date' => 'El camp :attribute no és una data vàlida.',
'date_format' => 'El camp :attribute no coincideix amb el format :format.',
'different' => 'Els camps :attribute i :other han de ser diferents.',
'digits' => 'El camp :attribute ha de tenir :digits dígits.',
'digits_between' => 'El camp :attribute ha de tenir entre :min i :max dígits.',
'email' => 'El camp :attribute ha de ser una adreça electrònica vàlida.',
'ends_with' => 'El camp :attribute ha dacabar amb un dels següents valors: :values',
'different' => 'El camp :attribute i :other han de ser diferents.',
'digits' => 'El camp :attribute ha de tenir :digits xifres.',
'digits_between' => 'El camp :attribute ha de tenir entre :min i :max xifres.',
'email' => 'El camp :attribute ha de ser un adreça electrònica vàlida.',
'ends_with' => 'El camp :attribute ha dacabar amb un dels signes següents: :values',
'file' => 'El camp :attribute ha de ser un fitxer vàlid.',
'filled' => 'El camp :attribute és obligatori.',
'gt' => [
@@ -41,74 +41,74 @@ return [
'array' => 'El camp :attribute ha de tenir més de :value elements.',
],
'gte' => [
'numeric' => 'El camp :attribute ha de ser més gran o igual que :value.',
'file' => 'El camp :attribute ha de tenir :value kilobytes o més.',
'string' => 'El camp :attribute ha de tenir :value caràcters o més.',
'array' => 'El camp :attribute ha de tenir :value elements o més.',
'numeric' => 'El camp :attribute ha de ser com a mínim :value.',
'file' => 'El camp :attribute ha de tenir com a mínim :value kilobytes.',
'string' => 'El camp :attribute ha de tenir com a mínim :value caràcters.',
'array' => 'El camp :attribute ha de tenir com a mínim :value elements.',
],
'exists' => 'El camp :attribute seleccionat no és vàlid.',
'image' => 'El camp :attribute ha de ser una imatge.',
'image_extension' => 'El camp :attribute ha de tenir una extensió dimatge vàlida i suportada.',
'in' => 'El camp :attribute seleccionat no és vàlid.',
'integer' => 'El camp :attribute ha de ser un enter.',
'ip' => 'El camp :attribute ha de ser una adreça IP vàlida.',
'ipv4' => 'El camp :attribute ha de ser una adreça IPv4 vàlida.',
'ipv6' => 'El camp :attribute ha de ser una adreça IPv6 vàlida.',
'image_extension' => 'El camp :attribute ha de tenir una extensió dimatge compatible.',
'in' => 'El camp :attribute no és vàlid.',
'integer' => 'El camp :attribute ha de ser un nombre enter.',
'ip' => 'El camp :attribute ha de ser un adreça IP vàlida.',
'ipv4' => 'El camp :attribute ha de ser un adreça IPv4 vàlida.',
'ipv6' => 'El camp :attribute ha de ser un adreça IPv6 vàlida.',
'json' => 'El camp :attribute ha de ser una cadena JSON vàlida.',
'lt' => [
'numeric' => 'El camp :attribute ha de ser menor que :value.',
'numeric' => 'El camp :attribute ha de ser més petit que :value.',
'file' => 'El camp :attribute ha de tenir menys de :value kilobytes.',
'string' => 'El camp :attribute ha de tenir menys de :value caràcters.',
'array' => 'El camp :attribute ha de tenir menys de :value elements.',
],
'lte' => [
'numeric' => 'El camp :attribute ha de ser més petit o igual que :value.',
'file' => 'El camp :attribute ha de tenir :value kilobytes o menys.',
'string' => 'El camp :attribute ha de tenir :value caràcters o menys.',
'array' => 'El camp :attribute ha de tenir :value elements o menys.',
'numeric' => 'El camp :attribute ha de ser com a màxim :value.',
'file' => 'El camp :attribute ha de tenir com a màxim :value kilobytes.',
'string' => 'El camp :attribute ha de tenir com a màxim :value caràcters.',
'array' => 'El camp :attribute ha de tenir com a màxim :value elements.',
],
'max' => [
'numeric' => 'El camp :attribute no pot ser més gran que :max.',
'file' => 'El camp :attribute no pot tenir més de :max kilobytes.',
'string' => 'El camp :attribute no pot tenir més de :max caràcters.',
'array' => 'El camp :attribute no pot tenir més de :max elements.',
'numeric' => 'El camp :attribute ha de ser com a màxim :max.',
'file' => 'El camp :attribute ha de tenir com a màxim :max kilobytes.',
'string' => 'El camp :attribute ha de tenir com a màxim :max caràcters.',
'array' => 'El camp :attribute ha de tenir com a màxim :max elements.',
],
'mimes' => 'El camp :attribute ha de ser un fitxer del tipus: :values.',
'min' => [
'numeric' => 'El camp :attribute no pot ser més petit que :min.',
'file' => 'El camp :attribute no pot tenir menys de :min kilobytes.',
'string' => 'El camp :attribute no pot tenir menys de :min caràcters.',
'array' => 'El camp :attribute no pot tenir menys de :min elements.',
'numeric' => 'El camp :attribute ha de ser com a mínim :min.',
'file' => 'El camp :attribute ha de tenir com a mínim :min kilobytes.',
'string' => 'El camp :attribute ha de tenir com a mínim :min caràcters.',
'array' => 'El camp :attribute ha de tenir com a mínim :min elements.',
],
'not_in' => 'El camp :attribute seleccionat no és vàlid.',
'not_regex' => 'El format del camp :attribute no és vàlid.',
'numeric' => 'El camp :attribute ha de ser un número.',
'regex' => 'El format del camp :attribute no és vàlid.',
'not_in' => 'El camp :attribute no és vàlid.',
'not_regex' => 'El format :attribute no és vàlid.',
'numeric' => 'El camp :attribute ha de ser un nombre.',
'regex' => 'El format :attribute no és vàlid.',
'required' => 'El camp :attribute és obligatori.',
'required_if' => 'El camp :attribute és obligatori quan :other és :value.',
'required_with' => 'El camp :attribute és obligatori quan hi ha aquest valor: :values.',
'required_with_all' => 'El camp :attribute és obligatori quan hi ha algun daquests valors: :values.',
'required_without' => 'El camp :attribute és obligatori quan no hi ha aquest valor: :values.',
'required_with' => 'El camp :attribute és obligatori quan hi ha :values.',
'required_with_all' => 'El camp :attribute és obligatori quan hi ha tots aquests valors: :values.',
'required_without' => 'El camp :attribute és obligatori quan no hi ha :values.',
'required_without_all' => 'El camp :attribute és obligatori quan no hi ha cap daquests valors: :values.',
'same' => 'Els camps :attribute i :other han de coincidir.',
'safe_url' => 'Lenllaç proporcionat podria no ser segur.',
'same' => 'El camp :attribute i :other han de coincidir.',
'safe_url' => 'És possible que lenllaç proporcionat no sigui segur.',
'size' => [
'numeric' => 'El camp :attribute ha de ser :size.',
'file' => 'El camp :attribute ha de tenir :size kilobytes.',
'string' => 'El camp :attribute ha de tenir :size caràcters.',
'array' => 'El camp :attribute ha de contenir :size elements.',
'string' => 'El camp :attribute ha de tenir :size caràcters',
'array' => 'El camp :attribute ha de tenir :size elements.',
],
'string' => 'El camp :attribute ha de ser una cadena.',
'timezone' => 'El camp :attribute ha de ser una zona vàlida.',
'totp' => 'El codi que heu proporcionat no és vàlid o ha caducat.',
'unique' => 'El camp :attribute ja es fa servir.',
'url' => 'El format del camp :attribute no és vàlid.',
'uploaded' => 'No sha pogut pujar el fitxer. És possible que el servidor no accepti fitxers daquesta mida.',
'string' => 'El camp :attribute ha de ser una cadena de text.',
'timezone' => 'El camp :attribute ha de ser un fus horari vàlid.',
'totp' => 'El codi proporcionat no és vàlid o ha caducat.',
'unique' => 'El camp :attribute ja sha utilitzat.',
'url' => 'El format :attribute no és vàlid.',
'uploaded' => 'No sha pogut pujar el fitxer. És possible que el servidor no admeti fitxers daquesta mida.',
// Custom validation lines
'custom' => [
'password-confirm' => [
'required_with' => 'Cal la confirmació de la contrasenya',
'required_with' => 'Heu de confirmar la contrasenya.',
],
],

View File

@@ -40,14 +40,14 @@ return [
'book_sort_notification' => 'Kniha byla úspěšně seřazena',
// Bookshelves
'bookshelf_create' => 'vytvořit knihovnu',
'bookshelf_create_notification' => 'Knihovna byla úspěšně vytvořena',
'bookshelf_create_from_book' => 'převést knihu na knihovnu',
'bookshelf_create_from_book_notification' => 'Kniha byla úspěšně převedena na knihovnu',
'bookshelf_update' => 'aktualizovat knihovnu',
'bookshelf_update_notification' => 'Knihovna byla úspěšně aktualizována',
'bookshelf_delete' => 'odstranit knihovnu',
'bookshelf_delete_notification' => 'Knihovna byla úspěšně smazána',
'bookshelf_create' => 'vytvořil polici',
'bookshelf_create_notification' => 'Police byla úspěšně vytvořena',
'bookshelf_create_from_book' => 'převést knihu na polici',
'bookshelf_create_from_book_notification' => 'Kniha byla úspěšně převedena na polici',
'bookshelf_update' => 'aktualizovat polici',
'bookshelf_update_notification' => 'Police byla úspěšně aktualizována',
'bookshelf_delete' => 'odstranil polici',
'bookshelf_delete_notification' => 'Police byla úspěšně odstraněna',
// Revisions
'revision_restore' => 'obnovil revizi',

View File

@@ -91,7 +91,7 @@ return [
'mfa_option_totp_title' => 'Mobilní aplikace',
'mfa_option_totp_desc' => 'Pro použití vícefaktorového ověření budete potřebovat mobilní aplikaci, která podporuje TOTP jako např. Google Authenticator, Authy nebo Microsoft Authenticator.',
'mfa_option_backup_codes_title' => 'Záložní kódy',
'mfa_option_backup_codes_desc' => 'Bezpečně si uložte sadu jednorázových záložních kódů, které můžete použít pro ověření vaší identity.',
'mfa_option_backup_codes_desc' => 'Vygeneruje sadu jednorázových záložních kódů, které zadáte při přihlášení k ověření své identity. Ujistěte se, že jsou uloženy na bezpečném místě.',
'mfa_gen_confirm_and_enable' => 'Potvrdit a povolit',
'mfa_gen_backup_codes_title' => 'Nastavení záložních kódů',
'mfa_gen_backup_codes_desc' => 'Uložte níže uvedený seznam kódů na bezpečné místo. Při přístupu k systému budete moci použít jeden z kódů jako druhou metodu ověření.',

View File

@@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Zásady ochrany osobních údajů',
'terms_of_service' => 'Podmínky služby',
// OpenSearch
'opensearch_description' => 'Vyhledat :appName',
];

View File

@@ -83,37 +83,37 @@ return [
'search_update' => 'Aktualizovat hledání',
// Shelves
'shelf' => 'Knihovna',
'shelves' => 'Knihovny',
'x_shelves' => '{0}:count knihoven|{1}:count knihovna|[2,4]:count knihovny|[5,*]:count knihoven',
'shelves_empty' => 'Nebyly vytvořeny žádné knihovny',
'shelves_create' => 'Vytvořit novou knihovnu',
'shelves_popular' => 'Populární knihovny',
'shelves_new' => 'Nové knihovny',
'shelves_new_action' => 'Nová Knihovna',
'shelves_popular_empty' => 'Nejpopulárnější knihovny se objeví zde.',
'shelves_new_empty' => 'Zde se zobrazí nejnověji vytvořené knihovny.',
'shelves_save' => 'Uložit knihovnu',
'shelves_books' => 'Knihy v této knihovně',
'shelves_add_books' => 'Přidat knihy do knihovny',
'shelves_drag_books' => 'Přetáhněte knihy níže a přidejte je do této knihovny',
'shelves_empty_contents' => 'Tato knihovna neobsahuje žádné knihy',
'shelves_edit_and_assign' => 'Upravit knihovnu a přiřadit knihy',
'shelves_edit_named' => 'Upravit knihovnu :name',
'shelves_edit' => 'Upravit knihovnu',
'shelves_delete' => 'Odstranit knihovnu',
'shelves_delete_named' => 'Odstranit knihovnu :name',
'shelves_delete_explain' => "Chystáte se smazat knihovnu ':name'. Knihy v ní obsažené zůstanou zachovány.",
'shelves_delete_confirmation' => 'Opravdu chcete odstranit tuto knihovnu?',
'shelves_permissions' => 'Oprávnění knihovny',
'shelves_permissions_updated' => 'Oprávnění knihovny byla aktualizována',
'shelves_permissions_active' => 'Oprávnění knihovny byla aktivována',
'shelves_permissions_cascade_warning' => 'Oprávnění v Knihovnách nejsou automaticky kaskádována do obsažených knih. To proto, že kniha může existovat ve více Knihovnách. Oprávnění však lze zkopírovat do podřízených knih pomocí níže uvedené možnosti.',
'shelf' => 'Police',
'shelves' => 'Police',
'x_shelves' => '{0}:count polic|{1}:count police|[2,4]:count police|[5,*]:count polic',
'shelves_empty' => 'Nebyly vytvořeny žádné police',
'shelves_create' => 'Vytvořit novou polici',
'shelves_popular' => 'Populární police',
'shelves_new' => 'Nové police',
'shelves_new_action' => 'Nová Police',
'shelves_popular_empty' => 'Nejpopulárnější police se objeví zde.',
'shelves_new_empty' => 'Zde se zobrazí nejnovější police.',
'shelves_save' => 'Uložit polici',
'shelves_books' => 'Knihy na této polici',
'shelves_add_books' => 'Přidat knihy do této police',
'shelves_drag_books' => 'Přetáhněte knihy níže a přidejte je do této police',
'shelves_empty_contents' => 'Tato police neobsahuje žádné knihy',
'shelves_edit_and_assign' => 'Upravit polici a přiřadit knihy',
'shelves_edit_named' => 'Upravit polici :name',
'shelves_edit' => 'Upravit polici',
'shelves_delete' => 'Odstranit polici',
'shelves_delete_named' => 'Odstranit polici :name',
'shelves_delete_explain' => "Chystáte se smazat polici ':name'. Knihy v ní obsažené zůstanou zachovány.",
'shelves_delete_confirmation' => 'Opravdu chcete odstranit tuto polici?',
'shelves_permissions' => 'Oprávnění police',
'shelves_permissions_updated' => 'Oprávnění police byla aktualizována',
'shelves_permissions_active' => 'Oprávnění police byla aktivována',
'shelves_permissions_cascade_warning' => 'Oprávnění v policiích nejsou automaticky kaskádována do obsažených knih. To proto, že kniha může existovat ve více policích. Oprávnění však lze zkopírovat do podřízených knih pomocí níže uvedené možnosti.',
'shelves_permissions_create' => 'Oprávnění k vytváření Shelf se používají pouze ke kopírování oprávnění do dětských knih pomocí níže uvedené akce. Nekontrolují schopnost vytvářet knihy.',
'shelves_copy_permissions_to_books' => 'Kopírovat oprávnění na knihy',
'shelves_copy_permissions' => 'Kopírovat oprávnění',
'shelves_copy_permissions_explain' => 'Toto použije aktuální nastavení oprávnění knihovny na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této knihovny.',
'shelves_copy_permission_success' => 'Oprávnění knihovny byla zkopírována na :count knih',
'shelves_copy_permissions_explain' => 'Tímto se použije aktuální nastavení oprávnění police na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této police.',
'shelves_copy_permission_success' => 'Oprávnění police byla zkopírována na :count knih',
// Books
'book' => 'Kniha',
@@ -224,6 +224,8 @@ return [
'pages_edit_switch_to_markdown_clean' => '(Vytvořený obsah)',
'pages_edit_switch_to_markdown_stable' => '(Stabilní obsah)',
'pages_edit_switch_to_wysiwyg' => 'Přepnout na WYSIWYG Editor',
'pages_edit_switch_to_new_wysiwyg' => 'Přepnout na nový WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(V alfa testování)',
'pages_edit_set_changelog' => 'Nastavit protokol změn',
'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli',
'pages_edit_enter_changelog' => 'Zadejte protokol změn',
@@ -303,7 +305,7 @@ return [
'page_tags' => 'Štítky stránky',
'chapter_tags' => 'Štítky kapitoly',
'book_tags' => 'Štítky knihy',
'shelf_tags' => 'Štítky knihovny',
'shelf_tags' => 'Štítky police',
'tag' => 'Štítek',
'tags' => 'Štítky',
'tags_index_desc' => 'Tagy mohou být použity pro obsah v rámci systému pro pružnou formu kategorizace. Tagy mohou mít klíč i hodnotu, přičemž hodnota je nepovinná. Po aplikaci může být obsah dotazován pomocí názvu a hodnoty štítku.',
@@ -316,12 +318,12 @@ return [
'tags_assigned_pages' => 'Přiřazeno ke stránkám',
'tags_assigned_chapters' => 'Přiřazeno ke kapitolám',
'tags_assigned_books' => 'Přiřazeno ke knihám',
'tags_assigned_shelves' => 'Přiřazeno ke knihovnám',
'tags_assigned_shelves' => 'Přiřazeno k policím',
'tags_x_unique_values' => ':count jedinečných hodnot',
'tags_all_values' => 'Všechny hodnoty',
'tags_view_tags' => 'Zobrazit štítky',
'tags_view_existing_tags' => 'Zobrazit existující štítky',
'tags_list_empty_hint' => 'Štítky mohou být přiřazeny pomocí postranního panelu editoru stránky nebo při úpravách podrobností knihy, kapitoly nebo knihovny.',
'tags_list_empty_hint' => 'Štítky mohou být přiřazeny pomocí postranního panelu editoru stránky nebo při úpravách podrobností knihy, kapitoly nebo police.',
'attachments' => 'Přílohy',
'attachments_explain' => 'Nahrajte soubory nebo připojte odkazy, které se zobrazí na stránce. Budou k nalezení v postranní liště.',
'attachments_explain_instant_save' => 'Změny zde provedené se okamžitě ukládají.',
@@ -395,9 +397,9 @@ return [
'copy_consider_access' => 'Po změně umístění, vlastníka nebo oprávnění může dojít k tomu, že obsah může být přístupný těm, kteří přístup dříve něměli.',
// Conversions
'convert_to_shelf' => 'Převést na knihovnu',
'convert_to_shelf_contents_desc' => 'Tuto knihu můžete převést na novou knihovnu se stejným obsahem. Kapitoly obsažené v této knize budou převedeny na nové knihy. Pokud tato kniha obsahuje jakékoli stránky, které nejsou uvedeny v kapitole, Tato kniha bude přejmenována a bude obsahovat tyto stránky a tato kniha se stane součástí nové knihovny.',
'convert_to_shelf_permissions_desc' => 'Veškerá oprávnění nastavená v této knize budou zkopírována do nové knihovny a do všech nových podřazených knih, které nemají vlastní oprávnění. Všimněte si, že oprávnění na regálech neobsahují automatickou kaskádu na obsah, jak to dělají pro knihy.',
'convert_to_shelf' => 'Převést na polici',
'convert_to_shelf_contents_desc' => 'Tuto knihu můžete převést na novou polici se stejným obsahem. Kapitoly obsažené v této knize budou převedeny na nové knihy. Pokud tato kniha obsahuje jakékoli stránky, které nejsou uvedeny v kapitole, bude tato kniha přejmenována a bude obsahovat tyto stránky a stane se součástí nové police.',
'convert_to_shelf_permissions_desc' => 'Veškerá oprávnění nastavená v této knize budou zkopírována do nové police a do všech nových podřazených knih, které nemají vlastní oprávnění. Všimněte si, že oprávnění na policích neobsahují automatickou kaskádu na obsah, jako je tomu u knih.',
'convert_book' => 'Převést knihu',
'convert_book_confirm' => 'Opravdu chcete převést tuto knihu?',
'convert_undo_warning' => 'To nelze tak snadno vrátit zpět.',

View File

@@ -9,7 +9,7 @@ return [
'permissionJson' => 'Nemáte povolení k provedení požadované akce.',
// Auth
'error_user_exists_different_creds' => 'Uživatel s emailem :email již existuje ale s jinými přihlašovacími údaji.',
'error_user_exists_different_creds' => 'Uživatel s emailem :email již existuje, ale s jinými přihlašovacími údaji.',
'auth_pre_register_theme_prevention' => 'Zadané údaje nedovolují zaregistrovat uživatelský účet',
'email_already_confirmed' => 'Emailová adresa již byla potvrzena. Zkuste se přihlásit.',
'email_confirmation_invalid' => 'Tento potvrzovací odkaz již neplatí nebo už byl použit. Zkuste prosím registraci znovu.',
@@ -37,6 +37,7 @@ return [
'social_driver_not_found' => 'Doplněk pro tohoto správce identity nebyl nalezen.',
'social_driver_not_configured' => 'Nastavení vašeho účtu na :socialAccount není správné. :socialAccount musí mít vaše svolení pro naší aplikaci vás přihlásit.',
'invite_token_expired' => 'Odkaz v pozvánce již bohužel vypršel. Namísto toho ale můžete zkusit resetovat heslo do Vašeho účtu.',
'login_user_not_found' => 'Uživatele pro tuto akci se nepodařilo najít.',
// System
'path_not_writable' => 'Nelze zapisovat na cestu k souboru :filePath. Zajistěte aby se dalo nahrávat na server.',
@@ -66,7 +67,7 @@ return [
// Entities
'entity_not_found' => 'Prvek nenalezen',
'bookshelf_not_found' => 'Knihovna nenalezena',
'bookshelf_not_found' => 'Police nenalezena',
'book_not_found' => 'Kniha nenalezena',
'page_not_found' => 'Stránka nenalezena',
'chapter_not_found' => 'Kapitola nenalezena',
@@ -77,6 +78,7 @@ return [
// Users
'users_cannot_delete_only_admin' => 'Nemůžete odstranit posledního administrátora',
'users_cannot_delete_guest' => 'Uživatele Host není možno odstranit',
'users_could_not_send_invite' => 'Nebylo možné vytvořit uživatele, protože se nepodařilo odeslat email s pozvánkou',
// Roles
'role_cannot_be_edited' => 'Tuto roli nelze editovat',

View File

@@ -5,23 +5,23 @@
return [
'new_comment_subject' => 'Nový komentář na stránce: :pageName',
'new_comment_intro' => 'Uživatel(ka) okomentoval(a) stránku v :appName:',
'new_page_subject' => 'New page: :pageName',
'new_page_intro' => 'Nová stránka byla vytvořena v :appName:',
'new_comment_intro' => 'Uživatel/ka okomentoval/a stránku v :appName:',
'new_page_subject' => 'Nová stránka: :pageName',
'new_page_intro' => 'V :appName byla vytvořena nová stránka:',
'updated_page_subject' => 'Aktualizovaná stránka: :pageName',
'updated_page_intro' => 'Stránka byla aktualizována v :appName:',
'updated_page_debounce' => 'Po nějakou dobu neobdržíte další oznámení o aktualizaci této stránky stejným editorem, aby se omezil počet stejných zaslaných upozornění.',
'updated_page_intro' => 'V :appName byla aktualizována stránka:',
'updated_page_debounce' => 'Po nějakou dobu neobdržíte další oznámení o aktualizaci této stránky stejným editorem, aby se omezil počet stejných zpráv.',
'detail_page_name' => 'Název stránky:',
'detail_page_path' => 'Umístěný v:',
'detail_commenter' => 'Komentoval(a):',
'detail_page_path' => 'Umístění:',
'detail_commenter' => 'Komentoval/a:',
'detail_comment' => 'Komentář:',
'detail_created_by' => 'Vytvořil(a):',
'detail_updated_by' => 'Aktualizoval(a):',
'detail_created_by' => 'Vytvořil/a:',
'detail_updated_by' => 'Aktualizoval/a:',
'action_view_comment' => 'Zobrazit komentář',
'action_view_page' => 'Zobrazit stránku',
'footer_reason' => 'Dle :link Vám bylo pro tento typ aktivity zasláno toto upozornění.',
'footer_reason' => 'Tato zpráva Vám byla doručena na základě Vašeho :link.',
'footer_reason_link' => 'nastavení upozornění',
];

View File

@@ -20,14 +20,14 @@ return [
'shortcuts_overview_desc' => 'Správa klávesových zkratek, které můžete použít k navigaci systémového uživatelského rozhraní.',
'notifications' => 'Nastavení upozornění',
'notifications_desc' => 'Ovládat e-mailová oznámení, která dostáváte při provádění určité aktivity v systému.',
'notifications_desc' => 'Nastavte si e-mailová oznámení, která dostanete při provedení určitých akcí v systému.',
'notifications_opt_own_page_changes' => 'Upozornit na změny stránek u kterých jsem vlastníkem',
'notifications_opt_own_page_comments' => 'Upozornit na komentáře na stránkách, které vlastním',
'notifications_opt_comment_replies' => 'Upozornit na odpovědi na mé komentáře',
'notifications_save' => 'Uložit preference',
'notifications_save' => 'Uložit nastavení',
'notifications_update_success' => 'Nastavení oznámení byla aktualizována!',
'notifications_watched' => 'Sledované a ignorované položky',
'notifications_watched_desc' => 'Níže jsou položky, které mají vlastní nastavení hodinek. Chcete-li aktualizovat vaše předvolby, podívejte se na položku a pak najděte možnosti hodinek v postranním panelu.',
'notifications_watched_desc' => 'Níže jsou položky, které mají vlastní nastavení sledování. Chcete-li aktualizovat vaše předvolby, zobrazte položku a pak upravte možnosti sledování v postranním panelu.',
'auth' => 'Přístup a zabezpečení',
'auth_change_password' => 'Změnit heslo',

Some files were not shown because too many files have changed in this diff Show More