2015-07-18 07:07:42 +03:00
{
"common" : {
2015-11-12 15:29:45 +03:00
"mail" : {
"title" : "Ghost at {domain}"
} ,
"seeLinkForInstructions" : "See {link} for instructions." ,
"time" : {
"seconds" : "seconds"
} ,
"api" : {
"authentication" : {
"sampleBlogDescription" : "Thoughts, stories and ideas." ,
"mail" : {
"resetPassword" : "Reset Password" ,
"checkEmailForInstructions" : "Check your email for further instructions." ,
"passwordChanged" : "Password changed successfully." ,
"invitationAccepted" : "Invitation accepted." ,
"yourNewGhostBlog" : "Your New Ghost Blog"
}
} ,
"mail" : {
"testGhostEmail" : "Test Ghost Email"
} ,
"users" : {
"mail" : {
"invitedByName" : "{invitedByName} has invited you to join {blogName}"
}
2016-08-06 13:55:45 +03:00
} ,
"clients" : {
"clientNotFound" : "Client not found"
2015-11-12 15:29:45 +03:00
}
}
2015-07-18 07:07:42 +03:00
} ,
"errors" : {
2015-11-12 15:29:45 +03:00
"apps" : {
"failedToParseActiveAppsSettings" : {
2017-04-24 20:41:00 +03:00
"error" : "Failed to parse active_apps setting value: {message}" ,
2015-11-12 15:29:45 +03:00
"context" : "Your apps will not be loaded." ,
2017-04-24 20:41:00 +03:00
"help" : "Check your settings table for typos in the active_apps value. It should look like: [\"app-1\", \"app2\"] (double quotes required)."
2015-11-12 15:29:45 +03:00
} ,
"appWillNotBeLoaded" : {
"error" : "The app will not be loaded" ,
"help" : "Check with the app creator, or read the app documentation for more details on app requirements"
} ,
"permissionsErrorLoadingApp" : {
"error" : "Error loading app named {name}; problem reading permissions: {message}"
} ,
"noInstallMethodLoadingApp" : {
"error" : "Error loading app named {name}; no install() method defined."
} ,
"noActivateMethodLoadingApp" : {
"error" : "Error loading app named {name}; no activate() method defined."
} ,
"accessResourceWithoutPermission" : {
"error" : "The App \"{name}\" attempted to perform an action or access a resource ({perm}.{method}) without permission."
} ,
"mustProvideAppName" : {
"error" : "Must provide an app name for api context"
} ,
"mustProvideAppPermissions" : {
"error" : "Must provide app permissions"
} ,
"unsafeAppRequire" : {
"error" : "Unsafe App require: {msg}"
}
} ,
"middleware" : {
2016-06-09 18:14:36 +03:00
"api" : {
2017-08-22 12:59:01 +03:00
"versionMismatch" : "Client request for {clientVersion} does not match server version {serverVersion}."
2016-06-09 18:14:36 +03:00
} ,
2015-11-12 15:29:45 +03:00
"auth" : {
2016-04-04 05:40:23 +03:00
"clientAuthenticationFailed" : "Client Authentication Failed" ,
2015-11-12 15:29:45 +03:00
"clientCredentialsNotProvided" : "Client credentials were not provided" ,
"clientCredentialsNotValid" : "Client credentials were not valid" ,
"forInformationRead" : "For information on how to fix this, please read {url}." ,
2018-10-10 05:27:09 +03:00
"unknownOrigin" : "Could not determine origin of request. Please ensure an Origin or Referrer header is present" ,
2018-10-02 11:35:23 +03:00
"mismatchedOrigin" : "Request made from incorrect origin." ,
"missingUserIDForSession" : "Cannot create session without user id." ,
2015-11-12 15:29:45 +03:00
"accessDenied" : "Access denied." ,
2016-03-27 01:28:00 +03:00
"pleaseSignIn" : "Please Sign In"
2015-11-12 15:29:45 +03:00
} ,
"oauth" : {
"invalidClient" : "Invalid client." ,
"invalidRefreshToken" : "Invalid refresh token." ,
"refreshTokenExpired" : "Refresh token expired."
} ,
"privateblogging" : {
"wrongPassword" : "Wrong password"
} ,
"spamprevention" : {
"tooManyAttempts" : "Too many attempts." ,
"noUsername" : "No username." ,
"noPassword" : "No password entered" ,
"tooManySigninAttempts" : {
"error" : "Only {rateSigninAttempts} tries per IP address every {rateSigninPeriod} seconds." ,
"context" : "Too many login attempts."
} ,
"tryAgainLater" : " Please try again later" ,
"waitOneHour" : " Please wait 1 hour." ,
"noEmail" : "No email." ,
"forgottenPasswordEmail" : {
"error" : "Only {rfa} forgotten password attempts per email every {rfp} seconds." ,
"context" : "Forgotten password reset attempt failed"
} ,
"forgottenPasswordIp" : {
"error" : "Only {rfa} tries per IP address every {rfp} seconds." ,
"context" : "Forgotten password reset attempt failed"
}
} ,
"themehandler" : {
2017-03-13 14:44:44 +03:00
"missingTheme" : "The currently active theme \"{theme}\" is missing." ,
2017-05-31 19:42:42 +03:00
"invalidTheme" : "The currently active theme \"{theme}\" is invalid." ,
2017-10-11 16:19:12 +03:00
"themeHasErrors" : "The currently active theme \"{theme}\" has errors, but will still work." ,
"activateFailed" : "Unable to activate the theme \"{theme}\"."
2017-09-21 18:01:03 +03:00
} ,
"redirects" : {
"register" : "Could not register custom redirects."
2015-11-12 15:29:45 +03:00
}
} ,
"utils" : {
"parsepackagejson" : {
"couldNotReadPackage" : "Could not read package.json file" ,
"nameOrVersionMissing" : "\"name\" or \"version\" is missing from theme package.json file." ,
"willBeRequired" : "This will be required in future. Please see {url}" ,
"themeFileIsMalformed" : "Theme package.json file is malformed"
🙇 Blog icon utils and publisher.logo for JSON-LD (#8297)
refs #8221, closes #7688, refs #7558
🙇 Improve meta data publisher logo behaviour
This is a follow-up PR for #8285.
Reasons: The code changes of #8285 caused error messages when falling back to the default `favicon.ico`, as the `image-size` tool doesn't support `ico` files.
This PR takes the logic to decide which logo needs to be listed in our schema into a new fn `blog_logo.js`. There we have now three decisions:
1. If we have a publication **logo**, we'll take that one
2. If we have no publication logo, but an **icon** we'll use this one.
3. If we have none of the above things, we fall back to our default `favicon.ico`
Additional, we're hard coding image dimensions for whenever the logo is an `.ico` file and built and extra decision to not call `image-size` when the dimension are already given.
I will create another follow-up PR, which checks the extension type for the file and offers it as a util.
🛠 Blog icon util
refs #7688
Serve functionality around the blog icon in its own util:
- getIconDimensions -> async function that takes the filepath of on ico file and returns its dimensions
- isIcoImageType -> returns true if file has `.ico` extension
- getIconType -> returns icon-type (`x-icon` or `png`)
- getIconUrl -> returns the absolut or relativ URL for the favicon: `[subdirectory or not]favicon.[ico or png]`
📖 Get .ico sizes for meta data & logo improvement
refs #7558
refs #8221
Use the new `blogIconUtil` in meta data to fetch the dimensions of `.ico` files.
Improvements for `publisher.logo`: We're now returning a hard-coded 'faked' image dimensions value to render an `imageObject` and prevent error our schema (Google structured data). As soon as an image (`.ico` or non-`.ico`) is too large, but - in case of non-`.ico` - a square format, be set the image-dimensions to 60px width and height. This reduces the chances of getting constantly error messages from Googles' webmaster tools.
- add getIconPath util
2017-04-11 19:32:06 +03:00
} ,
"blogIcon" : {
"error" : "Could not fetch icon dimensions."
2017-09-21 18:01:03 +03:00
} ,
2018-09-24 22:42:58 +03:00
"images" : {
"invalidDimensions" : "Could not fetch image dimensions."
} ,
2017-09-21 18:01:03 +03:00
"redirectsWrongFormat" : "Incorrect redirects file format."
2015-11-12 15:29:45 +03:00
} ,
"config" : {
"couldNotLocateConfigFile" : {
"error" : "Could not locate a configuration file." ,
"help" : "Please check your deployment for config.js or config.example.js."
} ,
"couldNotOpenForReading" : {
"error" : "Could not open {file} for read." ,
"help" : "Please check your deployment for config.js or config.example.js."
} ,
"couldNotOpenForWriting" : {
"error" : "Could not open {file} for write." ,
"help" : "Please check your deployment for config.js or config.example.js."
} ,
"invalidUrlInConfig" : {
"error" : "invalid site url" ,
"description" : "Your site url in config.js is invalid." ,
"help" : "Please make sure this is a valid url before restarting"
} ,
"urlCannotContainGhostSubdir" : {
"error" : "ghost subdirectory not allowed" ,
"description" : "Your site url in config.js cannot contain a subdirectory called ghost." ,
"help" : "Please rename the subdirectory before restarting"
} ,
2016-04-11 16:58:41 +03:00
"urlCannotContainPrivateSubdir" : {
"error" : "private subdirectory not allowed" ,
"description" : "Your site url in config.js cannot contain a subdirectory called private." ,
"help" : "Please rename the subdirectory before restarting"
} ,
2015-11-12 15:29:45 +03:00
"dbConfigInvalid" : {
"error" : "invalid database configuration" ,
"description" : "Your database configuration in config.js is invalid." ,
"help" : "Please make sure this is a valid Bookshelf database configuration"
} ,
"deprecatedProperty" : {
"error" : "The configuration property [{property}] has been deprecated." ,
"explanation" : "This will be removed in a future version, please update your config.js file." ,
"help" : "Please check {url} for the most up-to-date example."
} ,
"invalidServerValues" : {
"error" : "invalid server configuration" ,
"description" : "Your server values (socket, or host and port) in config.js are invalid." ,
"help" : "Please provide them before restarting."
}
} ,
"general" : {
2018-08-07 00:05:49 +03:00
"maintenance" : "Site is currently undergoing maintenance, please wait a moment then retry." ,
"maintenanceUrlService" : "Site is starting up, please wait a moment then retry." ,
2016-10-06 15:27:35 +03:00
"requiredOnFuture" : "This will be required in future. Please see {link}" ,
2017-09-21 18:01:03 +03:00
"internalError" : "Something went wrong." ,
"jsonParse" : "Could not parse JSON: {context}."
2015-11-12 15:29:45 +03:00
} ,
"httpServer" : {
"addressInUse" : {
"error" : "(EADDRINUSE) Cannot start Ghost." ,
"context" : "Port {port} is already in use by another program." ,
"help" : "Is another Ghost instance already running?"
} ,
"otherError" : {
"error" : "(Code: {errorNumber})" ,
"context" : "There was an error starting your server." ,
"help" : "Please use the error code above to search for a solution."
}
} ,
"mail" : {
"incompleteMessageData" : {
2017-09-19 16:24:20 +03:00
"error" : "Incomplete message data."
2015-11-12 15:29:45 +03:00
} ,
"failedSendingEmail" : {
2017-09-19 16:24:20 +03:00
"error" : "Failed to send email."
2015-11-12 15:29:45 +03:00
} ,
"noMailServerAtAddress" : {
2017-09-19 16:24:20 +03:00
"error" : " No mail server found at {domain}."
2015-11-12 15:29:45 +03:00
} ,
2017-09-19 16:24:20 +03:00
"reason" : " Reason: {reason}."
2015-11-12 15:29:45 +03:00
} ,
"models" : {
2016-04-29 13:58:40 +03:00
"subscriber" : {
"notEnoughPermission" : "You do not have permission to perform this action"
} ,
2015-11-12 15:29:45 +03:00
"post" : {
2018-02-21 18:59:48 +03:00
"postNotFound" : "Post not found." ,
2015-11-12 15:29:45 +03:00
"untitled" : "(Untitled)" ,
2016-04-14 14:22:38 +03:00
"valueCannotBeBlank" : "Value in {key} cannot be blank." ,
2016-05-19 14:49:22 +03:00
"isAlreadyPublished" : "Your post is already published, please reload your page." ,
"expectedPublishedAtInFuture" : "Date must be at least {cannotScheduleAPostBeforeInMinutes} minutes in the future." ,
2015-11-12 15:29:45 +03:00
"noUserFound" : "No user found" ,
"notEnoughPermission" : "You do not have permission to perform this action" ,
"tagUpdates" : {
"error" : "Unable to save tags." ,
"help" : "Your post was saved, but your tags were not updated."
}
} ,
"role" : {
2018-02-21 18:59:48 +03:00
"roleNotFound" : "Role not found" ,
2015-11-12 15:29:45 +03:00
"notEnoughPermission" : "You do not have permission to perform this action"
} ,
"settings" : {
"valueCannotBeBlank" : "Value in [settings.key] cannot be blank." ,
"unableToFindSetting" : "Unable to find setting to update: {key}" ,
"unableToFindDefaultSetting" : "Unable to find default setting: {key}"
} ,
"user" : {
"missingContext" : "missing context" ,
"onlyOneRolePerUserSupported" : "Only one role per user is supported at the moment." ,
"methodDoesNotSupportOwnerRole" : "This method does not support assigning the owner role" ,
2017-10-18 19:45:41 +03:00
"passwordDoesNotComplyLength" : "Your password must be at least {minLength} characters long." ,
2017-10-26 13:01:24 +03:00
"passwordDoesNotComplySecurity" : "Sorry, you cannot use an insecure password." ,
2015-11-12 15:29:45 +03:00
"notEnoughPermission" : "You do not have permission to perform this action" ,
"noUserWithEnteredEmailAddr" : "There is no user with that email address." ,
"userIsInactive" : "The user with that email address is inactive." ,
"userUpdateError" : {
2017-02-08 12:50:43 +03:00
"emailIsAlreadyInUse" : "Email is already in use" ,
2015-11-12 15:29:45 +03:00
"context" : "Error thrown from user update during login" ,
"help" : "Visit and save your profile after logging in to check for problems."
} ,
"incorrectPassword" : "Your password is incorrect." ,
"accountLocked" : "Your account is locked. Please reset your password to log in again by clicking the \"Forgotten password?\" link!" ,
2017-03-13 15:03:26 +03:00
"accountSuspended" : "Your account was suspended." ,
2015-11-12 15:29:45 +03:00
"newPasswordsDoNotMatch" : "Your new passwords do not match" ,
"passwordRequiredForOperation" : "Password is required for this operation" ,
"expiredToken" : "Expired token" ,
"tokenLocked" : "Token locked" ,
"invalidToken" : "Invalid token" ,
"userNotFound" : "User not found" ,
2017-07-31 12:37:37 +03:00
"ownerNotFound" : "Owner not found" ,
2015-11-12 15:29:45 +03:00
"onlyOwnerCanTransferOwnerRole" : "Only owners are able to transfer the owner role." ,
"onlyAdmCanBeAssignedOwnerRole" : "Only administrators can be assigned the owner role."
} ,
"base" : {
"index" : {
"missingContext" : "missing context"
} ,
"token" : {
"noUserFound" : "No user found" ,
"tokenNotFound" : "Token not found"
2017-10-04 11:56:09 +03:00
} ,
"invalidDate" : "Date format for `{key}` is invalid."
2015-11-12 15:29:45 +03:00
} ,
"plugins" : {
"filter" : {
"errorParsing" : "Error parsing filter" ,
"forInformationRead" : "For more information on how to use filter, see {url}"
}
}
} ,
"permissions" : {
"noActionsMapFound" : {
2016-05-19 14:49:22 +03:00
"error" : "No actions map found, ensure you have loaded permissions into database and then call permissions.init() before use."
2015-11-12 15:29:45 +03:00
} ,
"applyStatusRules" : {
"error" : "You do not have permission to retrieve {docName} with that status"
} ,
"noPermissionToAction" : "You do not have permission to perform this action"
} ,
2017-12-18 17:47:55 +03:00
"updateCheck" : {
2015-11-12 15:29:45 +03:00
"checkingForUpdatesFailed" : {
"error" : "Checking for updates failed, your blog will continue to function." ,
"help" : "If you get this error repeatedly, please seek help from {url}."
} ,
"unableToDecodeUpdateResponse" : {
"error" : "Unable to decode update response"
}
} ,
"api" : {
2016-11-07 14:18:50 +03:00
"common" : {
"invalidTokenStructure" : "Invalid token structure"
} ,
2015-11-12 15:29:45 +03:00
"authentication" : {
2016-03-06 21:18:33 +03:00
"setupUnableToRun" : "Database missing fixture data. Please reset database and try again." ,
2015-11-12 15:29:45 +03:00
"setupMustBeCompleted" : "Setup must be completed before making this request." ,
"noEmailProvided" : "No email provided." ,
2016-09-21 17:48:14 +03:00
"noTokenProvided" : "No token provided." ,
"noPasswordProvided" : "No password provided." ,
"noNameProvided" : "No name provided." ,
2015-11-12 15:29:45 +03:00
"invalidEmailReceived" : "The server did not receive a valid email" ,
"setupAlreadyCompleted" : "Setup has already been completed." ,
"unableToSendWelcomeEmail" : "Unable to send welcome email, your blog will continue to function." ,
"checkEmailConfigInstructions" : "Please see {url} for instructions on configuring email." ,
"notLoggedIn" : "You are not logged in." ,
"notTheBlogOwner" : "You are not the blog owner." ,
"invalidTokenTypeHint" : "Invalid token_type_hint given." ,
2016-03-06 21:18:33 +03:00
"invalidTokenProvided" : "Invalid token provided" ,
"tokenRevocationFailed" : "Token revocation failed"
2015-11-12 15:29:45 +03:00
} ,
"clients" : {
"clientNotFound" : "Client not found."
} ,
"configuration" : {
"invalidKey" : "Invalid key"
} ,
"db" : {
2016-08-18 22:25:51 +03:00
"missingFile" : "Please select a database file to import." ,
"invalidFile" : "Unsupported file. Please try any of the following formats: {extensions}" ,
2015-11-12 15:29:45 +03:00
"noPermissionToExportData" : "You do not have permission to export data (no rights)." ,
2016-08-18 22:25:51 +03:00
"noPermissionToImportData" : "You do not have permission to import data (no rights)."
2015-11-12 15:29:45 +03:00
} ,
"mail" : {
"noPermissionToSendEmail" : "You do not have permission to send mail." ,
"cannotFindCurrentUser" : "Could not find the current user"
} ,
"notifications" : {
"noPermissionToBrowseNotif" : "You do not have permission to browse notifications." ,
"noPermissionToAddNotif" : "You do not have permission to add notifications." ,
"noPermissionToDestroyNotif" : "You do not have permission to destroy notifications." ,
"noPermissionToDismissNotif" : "You do not have permission to dismiss this notification." ,
"notificationDoesNotExist" : "Notification does not exist."
} ,
"posts" : {
"postNotFound" : "Post not found."
} ,
2016-05-19 14:49:22 +03:00
"job" : {
"notFound" : "Job not found." ,
"publishInThePast" : "Use the force flag to publish a post in the past."
} ,
2017-09-22 22:57:43 +03:00
"redirects" : {
"missingFile" : "Please select a JSON file." ,
"invalidFile" : "Please select a valid JSON file to import."
} ,
2018-07-12 14:40:37 +03:00
"routes" : {
"missingFile" : "Please select a YAML file." ,
"invalidFile" : "Please select a valid YAML file to import."
} ,
2015-11-12 15:29:45 +03:00
"settings" : {
"problemFindingSetting" : "Problem finding setting: {key}" ,
"accessCoreSettingFromExtReq" : "Attempted to access core setting from external request" ,
2017-03-13 14:44:44 +03:00
"activeThemeSetViaAPI" : {
2017-04-24 20:41:00 +03:00
"error" : "Attempted to change active_theme via settings API" ,
2017-03-13 14:44:44 +03:00
"help" : "Please activate theme via the themes API endpoints instead"
} ,
2015-11-12 15:29:45 +03:00
"invalidJsonInLabs" : "Error: Invalid JSON in settings.labs" ,
"labsColumnCouldNotBeParsed" : "The column with key \"labs\" could not be parsed as JSON" ,
"tryUpdatingLabs" : "Please try updating a setting on the labs page, or manually editing your DB" ,
"noPermissionToEditSettings" : "You do not have permission to edit settings." ,
"noPermissionToReadSettings" : "You do not have permission to read settings."
} ,
"slugs" : {
"couldNotGenerateSlug" : "Could not generate slug." ,
"unknownSlugType" : "Unknown slug type '{type}'."
} ,
2016-05-08 17:49:12 +03:00
"subscribers" : {
2016-08-18 22:25:51 +03:00
"missingFile" : "Please select a csv." ,
"invalidFile" : "Please select a valid CSV file to import." ,
2016-05-08 17:49:12 +03:00
"subscriberNotFound" : "Subscriber not found." ,
2016-08-18 22:25:51 +03:00
"subscriberAlreadyExists" : "Email address is already subscribed."
2016-05-12 18:37:45 +03:00
} ,
"tags" : {
"tagNotFound" : "Tag not found."
2016-04-14 23:44:05 +03:00
} ,
2015-11-12 15:29:45 +03:00
"themes" : {
"noPermissionToBrowseThemes" : "You do not have permission to browse themes." ,
"noPermissionToEditThemes" : "You do not have permission to edit themes." ,
"themeDoesNotExist" : "Theme does not exist." ,
2016-08-24 15:45:54 +03:00
"invalidTheme" : "Theme is not compatible or contains errors." ,
2018-04-18 16:05:20 +03:00
"invalidThemeName" : "Please select a valid theme." ,
2016-08-23 15:07:25 +03:00
"missingFile" : "Please select a theme." ,
"invalidFile" : "Please select a valid zip file." ,
"overrideCasper" : "Please rename your zip, it's not allowed to override the default casper theme." ,
2017-03-13 14:44:44 +03:00
"destroyCasper" : "Deleting the default casper theme is not allowed." ,
"destroyActive" : "Deleting the active theme is not allowed."
2015-11-12 15:29:45 +03:00
} ,
2016-08-18 22:25:51 +03:00
"images" : {
"missingFile" : "Please select an image." ,
2018-09-24 22:42:58 +03:00
"invalidFile" : "Please select a valid image." ,
"isNotSquare" : "Please select a valid image file with square dimensions."
2015-11-12 15:29:45 +03:00
} ,
2017-01-26 12:01:52 +03:00
"icons" : {
"missingFile" : "Please select an icon." ,
2017-04-11 14:40:17 +03:00
"invalidFile" : "Blog icon must be a square .ico or .png file between 60px – 1,000px, under 100kb." ,
2017-01-26 12:01:52 +03:00
"couldNotGetSize" : "Couldn/'t get icon dimensions"
} ,
2015-11-12 15:29:45 +03:00
"users" : {
"userNotFound" : "User not found." ,
"cannotChangeOwnRole" : "You cannot change your own role." ,
2017-03-13 15:03:26 +03:00
"cannotChangeStatus" : "You cannot change your own status." ,
2015-11-12 15:29:45 +03:00
"cannotChangeOwnersRole" : "Cannot change Owner's role" ,
"noPermissionToEditUser" : "You do not have permission to edit this user" ,
"noPermissionToAddUser" : "You do not have permission to add this user" ,
"noEmailProvided" : "No email provided." ,
"userAlreadyRegistered" : "User is already registered." ,
"noPermissionToDestroyUser" : "You do not have permission to destroy this user." ,
"noPermissionToChangeUsersPwd" : "You do not have permission to change the password for this user"
} ,
"utils" : {
"noPermissionToCall" : "You do not have permission to {method} {docName}" ,
"noRootKeyProvided" : "No root key ('{docName}') provided." ,
2018-03-27 17:16:15 +03:00
"invalidStructure" : "No valid object structure provided for: {key}" ,
2015-11-12 15:29:45 +03:00
"invalidIdProvided" : "Invalid id provided."
2016-09-21 17:48:14 +03:00
} ,
"invites" : {
"inviteNotFound" : "Invite not found." ,
"inviteExpired" : "Invite is expired." ,
"emailIsRequired" : "E-Mail is required." ,
"roleIsRequired" : "Role is required" ,
2016-11-16 12:33:44 +03:00
"roleNotFound" : "Role not found" ,
2016-09-21 17:48:14 +03:00
"errorSendingEmail" : {
"error" : "Error sending email: {message}" ,
"help" : "Please check your email settings and resend the invitation."
} ,
2017-01-25 15:07:31 +03:00
"notAllowedToInviteOwner" : "Not allowed to invite an owner user." ,
"notAllowedToInvite" : "Not allowed to invite this role."
2017-11-21 18:43:14 +03:00
} ,
"webhooks" : {
"webhookAlreadyExists" : "A webhook for requested event with supplied target_url already exists."
2018-06-05 17:49:23 +03:00
} ,
"oembed" : {
"noUrlProvided" : "No url provided." ,
"unknownProvider" : "No provider found for supplied URL."
2015-11-12 15:29:45 +03:00
}
} ,
"data" : {
"export" : {
"errorExportingData" : "Error exporting data"
} ,
"import" : {
"dataImporter" : {
"unableToFindOwner" : "Unable to find an owner"
} ,
"index" : {
"duplicateEntryFound" : "Duplicate entry found. Multiple values of '{value}' found for {offendingProperty}."
} ,
"utils" : {
"dataLinkedToUnknownUser" : "Attempting to import data linked to unknown user id {userToMap}"
}
} ,
"importer" : {
"index" : {
"couldNotCleanUpFile" : {
"error" : "Import could not clean up file " ,
"context" : "Your blog will continue to work as expected"
} ,
"unsupportedRoonExport" : "Your zip file looks like an old format Roon export, please re-export your Roon blog and try again." ,
"noContentToImport" : "Zip did not include any content to import." ,
"invalidZipStructure" : "Invalid zip file structure." ,
"invalidZipFileBaseDirectory" : "Invalid zip file: base directory read failed" ,
"zipContainsMultipleDataFormats" : "Zip file contains multiple data formats. Please split up and import separately."
} ,
"handlers" : {
"json" : {
"invalidJsonFormat" : "Invalid JSON format, expected `{ db: [exportedData] }`" ,
"apiDbImportContent" : "API DB import content" ,
"checkImportJsonIsValid" : "check that the import file is valid JSON." ,
"failedToParseImportJson" : "Failed to parse the import JSON file."
}
}
2017-10-25 17:27:56 +03:00
}
} ,
"services" : {
"ping" : {
"requestFailed" : {
"error" : "The {service} service was unable to send a ping request, your blog will continue to function." ,
"help" : "If you get this error repeatedly, please seek help on {url}."
2015-11-12 15:29:45 +03:00
}
YAML settings loader and parser
closes #9528
These code changes introduce a YAML parser which will load and parse YAML files from the `/content/settings` directory. There are three major parts involved:
1. `ensure-settings.js`: this fn takes care that on bootstrap, the supported files are present in the `/content/settings` directory. If the files are not present, they get copied back from our default files. The default files to copy from are located in `core/server/services/settings`.
2. `loader.js`: the settings loader reads the requested `yaml` file from the disk and passes it to the yaml parser, which returns a `json` object of the file. The settings loader throws an error, if the file is not accessible, e. g. because of permission errors.
3. `yaml-parser`: gets passed a `yaml` file and returns a `json` object. If the file is not parseable, it returns a clear error that contains the information, what and where the parsing error occurred (e. g. line number and reason).
- added a `get()` fn to settings services, that returns the settings object that's asked for. e. g. `settings.get('routes').then(()...` will return the `routes` settings.
- added a `getAll()` fn to settings services, that returns all available settings in an object. The object looks like: `{routes: {routes: {}, collections: {}, resources: {}}, globals: {value: {}}`, assuming that we have to supported settings `routes` and `globals`.
Further additions:
- config `contentPath` for `settings`
- config overrides for default `yaml` files location in `/core/server/services/settings`
**Important**: These code changes are in preparation for Dynamic Routing and not yet used. The process of copying the supported `yaml` files (in this first step, the `routes.yaml` file) is not yet activated.
2018-04-13 04:34:03 +03:00
} ,
"settings" : {
"yaml" : {
"error" : "Could not parse {file}: {context}." ,
✨Dynamic Routing Beta (#9596)
refs #9601
### Dynamic Routing
This is the beta version of dynamic routing.
- we had a initial implementation of "channels" available in the codebase
- we have removed and moved this implementation
- there is now a centralised place for dynamic routing - server/services/routing
- each routing component is represented by a router type e.g. collections, routes, static pages, taxonomies, rss, preview of posts
- keep as much as possible logic of routing helpers, middlewares and controllers
- ensure test coverage
- connect all the things together
- yaml file + validation
- routing + routers
- url service
- sitemaps
- url access
- deeper implementation of yaml validations
- e.g. hard require slashes
- ensure routing hierarchy/order
- e.g. you enable the subscriber app
- you have a custom static page, which lives under the same slug /subscribe
- static pages are stronger than apps
- e.g. the first collection owns the post it has filtered
- a post cannot live in two collections
- ensure apps are still working and hook into the routers layer (or better said: and register in the routing service)
- put as much as possible comments to the code base for better understanding
- ensure a clean debug log
- ensure we can unmount routes
- e.g. you have a collection permalink of /:slug/ represented by {globals.permalink}
- and you change the permalink in the admin to dated permalink
- the express route get's refreshed from /:slug/ to /:year/:month/:day/:slug/
- unmount without server restart, yey
- ensure we are backwards compatible
- e.g. render home.hbs for collection index if collection route is /
- ensure you can access your configured permalink from the settings table with {globals.permalink}
### Render 503 if url service did not finish
- return 503 if the url service has not finished generating the resource urls
### Rewrite sitemaps
- we have rewritten the sitemaps "service", because the url generator does no longer happen on runtime
- we generate all urls on bootstrap
- the sitemaps service will consume created resource and router urls
- these urls will be shown on the xml pages
- we listen on url events
- we listen on router events
- we no longer have to fetch the resources, which is nice
- the urlservice pre-fetches resources and emits their urls
- the urlservice is the only component who knows which urls are valid
- i made some ES6 adaptions
- we keep the caching logic -> only regenerate xml if there is a change
- updated tests
- checked test coverage (100%)
### Re-work usage of Url utility
- replace all usages of `urlService.utils.urlFor` by `urlService.getByResourceId`
- only for resources e.g. post, author, tag
- this is important, because with dynamic routing we no longer create static urls based on the settings permalink on runtime
- adapt url utility
- adapt tests
2018-06-05 20:02:20 +03:00
"help" : "Check your {file} file for typos and fix the named issues." ,
"validate" : "The following definition \"{at}\" is invalid: {reason}"
YAML settings loader and parser
closes #9528
These code changes introduce a YAML parser which will load and parse YAML files from the `/content/settings` directory. There are three major parts involved:
1. `ensure-settings.js`: this fn takes care that on bootstrap, the supported files are present in the `/content/settings` directory. If the files are not present, they get copied back from our default files. The default files to copy from are located in `core/server/services/settings`.
2. `loader.js`: the settings loader reads the requested `yaml` file from the disk and passes it to the yaml parser, which returns a `json` object of the file. The settings loader throws an error, if the file is not accessible, e. g. because of permission errors.
3. `yaml-parser`: gets passed a `yaml` file and returns a `json` object. If the file is not parseable, it returns a clear error that contains the information, what and where the parsing error occurred (e. g. line number and reason).
- added a `get()` fn to settings services, that returns the settings object that's asked for. e. g. `settings.get('routes').then(()...` will return the `routes` settings.
- added a `getAll()` fn to settings services, that returns all available settings in an object. The object looks like: `{routes: {routes: {}, collections: {}, resources: {}}, globals: {value: {}}`, assuming that we have to supported settings `routes` and `globals`.
Further additions:
- config `contentPath` for `settings`
- config overrides for default `yaml` files location in `/core/server/services/settings`
**Important**: These code changes are in preparation for Dynamic Routing and not yet used. The process of copying the supported `yaml` files (in this first step, the `routes.yaml` file) is not yet activated.
2018-04-13 04:34:03 +03:00
} ,
"loader" : "Error trying to load YAML setting for {setting} from '{path}'." ,
"ensureSettings" : "Error trying to access settings files in {path}."
2015-11-12 15:29:45 +03:00
}
} ,
"errors" : {
"noMessageSupplied" : "no message supplied" ,
"error" : "\nERROR:" ,
"warning" : "\nWarning:" ,
"anErrorOccurred" : "An error occurred" ,
"unknownErrorOccurred" : "An unknown error occurred." ,
"unknownError" : "Unknown Error" ,
"unknownApiError" : "Unknown API Error" ,
"databaseIsReadOnly" : "Your database is in read only mode. Visitors can read your blog, but you can't log in or add posts." ,
"checkDatabase" : "Check your database file and make sure that file owner and permissions are correct." ,
"notEnoughPermission" : "You do not have permission to perform this action" ,
"errorWhilstRenderingError" : "Error whilst rendering error page" ,
"errorTemplateHasError" : "Error template has an error" ,
"oopsErrorTemplateHasError" : "Oops, seems there is an error in the error template." ,
"encounteredError" : "Encountered the error: " ,
"whilstTryingToRender" : "whilst trying to render an error page for the error: " ,
"renderingErrorPage" : "Rendering Error Page" ,
"caughtProcessingError" : "Ghost caught a processing error in the middleware layer." ,
2017-08-10 16:31:52 +03:00
"imageNotFound" : "Image not found" ,
2017-11-28 17:27:18 +03:00
"imageNotFoundWithRef" : "Image not found: {img}" ,
"cannotReadImage" : "Could not read image: {img}" ,
🎉 🎨 ✨ Remove middleware/index.js (#7548)
closes #4172, closes #6948, refs #7491, refs #7488, refs #7542, refs #7484
* 🎨 Co-locate all admin-related code in /admin
- move all the admin related code from controllers, routes and helpers into a single location
- add error handling middleware explicitly to adminApp
- re-order blogApp middleware to ensure the shared middleware is mounted after the adminApp
- TODO: rethink the structure of /admin, this should probably be an internal app
* 💄 Group global middleware together
- There are only a few pieces of middleware which are "global"
- These are needed for the admin, blog and api
- Everything else is only needed in one or two places
* ✨ Introduce a separate blogApp
- create a brand-new blogApp
- mount all blog/theme only middleware etc onto blogApp
- mount error handling on blogApp only
* 🎨 Separate error handling for HTML & API JSON
- split JSON and HTML error handling into separate functions
- re-introduce a way to not output the stack for certain errors
- add more tests around errors & an assertion framework for checking JSON Errors
- TODO: better 404 handling for static assets
Rationale:
The API is very different to the blog/admin panel:
- It is intended to only ever serve JSON, never HTML responses
- It is intended to always serve JSON
Meanwhile the blog and admin panel have no need for JSON errors,
when an error happens on those pages, we should serve HTML pages
which are nicely formatted with the error & using the correct template
* 🐛 Fix checkSSL to work for subapps
- in order to make this work on a sub app we need to use the pattern `req.originalUrl || req.url`
* 🔥 Get rid of decide-is-admin (part 1/2)
- delete decide-is-admin & tests
- add two small functions to apiApp and adminApp to set res.isAdmin
- mount checkSSL on all the apps
- TODO: deduplicate the calls to checkSSL by making blogApp a subApp :D
- PART 2/2: finish cleaning this up by removing it from where it's not needed and giving it a more specific name
Rationale:
Now that we have both an adminApp and an apiApp,
we can temporarily replace this weird path-matching middleware
with middleware that sets res.isAdmin for api & admin
* 🎨 Wire up prettyURLs on all Apps
- prettyURLs is needed for all requests
- it cannot be global because it has to live after asset middleware, and before routing
- this does not result in duplicate redirects, but does result in duplicate checks
- TODO: resolve extra middleware in stack by making blogApp a sub app
* ⏱ Add debug to API setup
* 🎨 Rename blogApp -> parentApp in middleware
* 🎨 Co-locate all blog-related code in /blog
- Move all of the blogApp code from middleware/index.js to blog/app.js
- Move routes/frontend.js to blog/routes.js
- Remove the routes/index.js and routes folder, this is empty now!
- @TODO is blog the best name for this? 🤔
- @TODO sort out the big hunk of asset-related mess
- @TODO also separate out the concept of theme from blog
* 🎉 Replace middleware index with server/app.js
- The final piece of the puzzle! 🎉 🎈 🎂
- We no longer have our horrendous middleware/index.js
- Instead, we have a set of app.js files, which all use a familiar pattern
* 💄 Error handling fixups
2016-10-13 18:24:09 +03:00
"pageNotFound" : "Page not found" ,
"resourceNotFound" : "Resource not found"
2015-11-12 15:29:45 +03:00
}
2015-07-18 07:07:42 +03:00
} ,
"warnings" : {
2015-11-12 15:29:45 +03:00
"index" : {
"usingDirectMethodToSendEmail" : "Ghost is attempting to use a direct method to send email. \nIt is recommended that you explicitly configure an email service." ,
"unableToSendEmail" : "Ghost is currently unable to send email."
} ,
"helpers" : {
2016-04-21 18:37:52 +03:00
"helperNotAvailable" : "The \\{\\{{helperName}\\}\\} helper is not available." ,
2017-08-15 11:50:36 +03:00
"flagMustBeEnabled" : "The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper." ,
2016-04-21 18:37:52 +03:00
"seeLink" : "See {url}" ,
2017-08-27 19:00:32 +03:00
"mustBeCalledAsBlock" : "The \\{\\{{helperName}\\}\\} helper must be called as a block. E.g. \\{\\{#{helperName}\\}\\} \\{\\{/{helperName}\\}\\}" ,
2015-11-12 15:29:45 +03:00
"foreach" : {
"iteratorNeeded" : "Need to pass an iterator to #foreach"
} ,
"get" : {
2017-03-23 22:00:58 +03:00
"invalidResource" : "Invalid resource given to get helper"
2015-11-12 15:29:45 +03:00
} ,
"has" : {
"invalidAttribute" : "Invalid or no attribute given to has helper"
} ,
"index" : {
"missingHelper" : "Missing helper: '{arg}'"
} ,
"is" : {
"invalidAttribute" : "Invalid or no attribute given to is helper"
} ,
"navigation" : {
"invalidData" : "navigation data is not an object or is a function" ,
"valuesMustBeDefined" : "All values must be defined for label, url and current" ,
"valuesMustBeString" : "Invalid value, Url and Label must be strings"
} ,
"page_url" : {
"isDeprecated" : "Warning: pageUrl is deprecated, please use page_url instead\nThe helper pageUrl has been replaced with page_url in Ghost 0.4.2, and will be removed entirely in Ghost 0.6\nIn your theme's pagination.hbs file, pageUrl should be renamed to page_url"
} ,
"pagination" : {
2018-07-24 12:35:33 +03:00
"invalidData" : "The \\{\\{pagination\\}\\} helper was used outside of a paginated context. See https://themes.ghost.org/docs/pagination." ,
2015-11-12 15:29:45 +03:00
"valuesMustBeDefined" : "All values must be defined for page, pages, limit and total" ,
"nextPrevValuesMustBeNumeric" : "Invalid value, Next/Prev must be a number" ,
"valuesMustBeNumeric" : "Invalid value, check page, pages, limit and total are numbers"
} ,
"plural" : {
"valuesMustBeDefined" : "All values must be defined for empty, singular and plural"
} ,
2017-04-24 20:21:47 +03:00
"img_url" : {
2017-08-27 20:06:53 +03:00
"attrIsRequired" : "Attribute is required e.g. \\{\\{img_url feature_image\\}\\}" ,
"attrIsUnknown" : "Attribute passed to \\{\\{img_url\\}\\} is unknown"
2017-04-24 20:21:47 +03:00
} ,
2015-11-12 15:29:45 +03:00
"template" : {
"templateNotFound" : "Template {name} not found."
}
}
2015-07-18 07:07:42 +03:00
} ,
"notices" : {
2015-11-12 15:29:45 +03:00
"index" : {
2018-02-07 11:35:48 +03:00
"welcomeToGhost" : "Welcome to Ghost."
2015-11-12 15:29:45 +03:00
} ,
"httpServer" : {
"cantTouchThis" : "Can't touch this" ,
"ghostIsRunning" : "Ghost is running..." ,
2017-07-11 11:00:07 +03:00
"yourBlogIsAvailableOn" : "Your blog is now available on {url}" ,
"ctrlCToShutDown" : "Ctrl+C to shut down" ,
2015-11-12 15:29:45 +03:00
"ghostIsRunningIn" : "Ghost is running in {env}..." ,
2017-07-11 11:00:07 +03:00
"listeningOn" : "Listening on: {host}:{port}" ,
"urlConfiguredAs" : "Url configured as: {url}" ,
"ghostHasShutdown" : "Ghost has shut down" ,
"yourBlogIsNowOffline" : "Your blog is now offline" ,
"ghostWasRunningFor" : "Ghost was running for" ,
2015-11-12 15:29:45 +03:00
"ghostIsClosingConnections" : "Ghost is closing connections"
} ,
"mail" : {
"messageSent" : "Message sent. Double check inbox and spam folder!"
} ,
"api" : {
"users" : {
"pwdChangedSuccessfully" : "Password changed successfully."
}
} ,
"data" : {
"fixtures" : {
2018-02-07 11:35:48 +03:00
"jQueryRemoved" : "jQuery has been removed from Ghost core and is now being loaded from the jQuery Foundation's CDN."
2015-11-12 15:29:45 +03:00
} ,
"utils" : {
"index" : {
"noSupportForDatabase" : "No support for database client {client}"
}
} ,
"validation" : {
"index" : {
"valueCannotBeBlank" : "Value in [{tableName}.{columnKey}] cannot be blank." ,
2017-05-23 19:18:13 +03:00
"valueMustBeBoolean" : "Value in [{tableName}.{columnKey}] must be one of true, false, 0 or 1." ,
2015-11-12 15:29:45 +03:00
"valueExceedsMaxLength" : "Value in [{tableName}.{columnKey}] exceeds maximum length of {maxlength} characters." ,
"valueIsNotInteger" : "Value in [{tableName}.{columnKey}] is not an integer." ,
"themeCannotBeActivated" : "{themeName} cannot be activated because it is not currently installed." ,
Added more database soft limits (#9225)
refs #8143
Sets soft limits for certain db fields:
- `posts`:
- `title`: 255 chars (current hard limit: 2,000 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- `users`:
- `bio`: 200 chars (current hard limit: 65,535 chars)
- `location`: 150 chars (current hard limit: 65,535 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `tags`:
- `description`: 500 chars (current hard limit: 65,535 chars)
- `meta_title`: 300 chars (current hard limit: 2,000 chars)
- `meta_description`: 500 chars (current hard limit: 2,000 chars)
- same error message for isLength validator as for hard limits (more improvements are comming with https://github.com/TryGhost/Ghost/issues/6050)
- added more tests for importer
- added dynamic translation key handling for validators errors (isLength is only supported atm)
2017-11-09 17:22:20 +03:00
"validationFailed" : "Validation ({validationName}) failed for {key}" ,
"validationFailedTypes" : {
"isLength" : "Value in [{tableName}.{key}] exceeds maximum length of {max} characters."
}
2015-11-12 15:29:45 +03:00
}
}
}
2015-07-18 07:07:42 +03:00
}
}