diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index a1232efc6..436734816 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -1,21 +1,15 @@ 'These credentials do not match our records.', 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', - /** - * Login & Register - */ + // Login & Register 'sign_up' => 'Sign up', 'log_in' => 'Log in', 'log_in_with' => 'Login with :socialDriver', @@ -43,23 +37,18 @@ return [ 'register_success' => 'Thanks for signing up! You are now registered and signed in.', - /** - * Password Reset - */ + // Password Reset 'reset_password' => 'Reset Password', 'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.', 'reset_password_send_button' => 'Send Reset Link', 'reset_password_sent_success' => 'A password reset link has been sent to :email.', 'reset_password_success' => 'Your password has been successfully reset.', - 'email_reset_subject' => 'Reset your :appName password', 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.', 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.', - /** - * Email Confirmation - */ + // Email Confirmation 'email_confirm_subject' => 'Confirm your email on :appName', 'email_confirm_greeting' => 'Thanks for joining :appName!', 'email_confirm_text' => 'Please confirm your email address by clicking the button below:', diff --git a/resources/lang/en/common.php b/resources/lang/en/common.php index 8e86129e2..ac2edc621 100644 --- a/resources/lang/en/common.php +++ b/resources/lang/en/common.php @@ -1,9 +1,10 @@ 'Cancel', 'confirm' => 'Confirm', 'back' => 'Back', @@ -12,18 +13,14 @@ return [ 'select' => 'Select', 'more' => 'More', - /** - * Form Labels - */ + // Form Labels 'name' => 'Name', 'description' => 'Description', 'role' => 'Role', 'cover_image' => 'Cover image', 'cover_image_description' => 'This image should be approx 440x250px.', - /** - * Actions - */ + // Actions 'actions' => 'Actions', 'view' => 'View', 'create' => 'Create', @@ -40,9 +37,7 @@ return [ 'remove' => 'Remove', 'add' => 'Add', - /** - * Misc - */ + // Misc 'deleted_user' => 'Deleted User', 'no_activity' => 'No activity to show', 'no_items' => 'No items available', @@ -54,15 +49,11 @@ return [ 'list_view' => 'List View', 'default' => 'Default', - /** - * Header - */ + // Header 'view_profile' => 'View Profile', 'edit_profile' => 'Edit Profile', - /** - * Email Content - */ + // Email Content 'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:', 'email_rights' => 'All rights reserved', ]; \ No newline at end of file diff --git a/resources/lang/en/components.php b/resources/lang/en/components.php index c093f7316..d8e8981fb 100644 --- a/resources/lang/en/components.php +++ b/resources/lang/en/components.php @@ -1,9 +1,10 @@ 'Image Select', 'image_all' => 'All', 'image_all_title' => 'View all images', @@ -24,9 +25,7 @@ return [ 'image_delete_success' => 'Image successfully deleted', 'image_upload_remove' => 'Remove', - /** - * Code editor - */ + // Code Editor 'code_editor' => 'Edit Code', 'code_language' => 'Code Language', 'code_content' => 'Code Content', diff --git a/resources/lang/en/entities.php b/resources/lang/en/entities.php index 810b25f45..2a64f57a3 100644 --- a/resources/lang/en/entities.php +++ b/resources/lang/en/entities.php @@ -1,9 +1,11 @@ 'Recently Created', 'recently_created_pages' => 'Recently Created Pages', 'recently_updated_pages' => 'Recently Updated Pages', @@ -31,17 +33,13 @@ return [ 'export_pdf' => 'PDF File', 'export_text' => 'Plain Text File', - /** - * Permissions and restrictions - */ + // Permissions and restrictions 'permissions' => 'Permissions', 'permissions_intro' => 'Once enabled, These permissions will take priority over any set role permissions.', 'permissions_enable' => 'Enable Custom Permissions', 'permissions_save' => 'Save Permissions', - /** - * Search - */ + // Search 'search_results' => 'Search Results', 'search_total_results_found' => ':count result found|:count total results found', 'search_clear' => 'Clear Search', @@ -66,9 +64,7 @@ return [ 'search_set_date' => 'Set Date', 'search_update' => 'Update Search', - /** - * Shelves - */ + // Shelves 'shelf' => 'Shelf', 'shelves' => 'Shelves', 'shelves_long' => 'Bookshelves', @@ -98,9 +94,7 @@ return [ 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.', 'shelves_copy_permission_success' => 'Bookshelf permissions copied to :count books', - /** - * Books - */ + // Books 'book' => 'Book', 'books' => 'Books', 'x_books' => ':count Book|:count Books', @@ -134,9 +128,7 @@ return [ 'books_sort_show_other' => 'Show Other Books', 'books_sort_save' => 'Save New Order', - /** - * Chapters - */ + // Chapters 'chapter' => 'Chapter', 'chapters' => 'Chapters', 'x_chapters' => ':count Chapter|:count Chapters', @@ -159,9 +151,7 @@ return [ 'chapters_permissions_success' => 'Chapter Permissions Updated', 'chapters_search_this' => 'Search this chapter', - /** - * Pages - */ + // Pages 'page' => 'Page', 'pages' => 'Pages', 'x_pages' => ':count Page|:count Pages', @@ -235,9 +225,7 @@ return [ 'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content', 'pages_specific' => 'Specific Page', - /** - * Editor sidebar - */ + // Editor Sidebar 'page_tags' => 'Page Tags', 'chapter_tags' => 'Chapter Tags', 'book_tags' => 'Book Tags', @@ -273,18 +261,14 @@ return [ 'attachments_file_updated' => 'File successfully updated', 'attachments_link_attached' => 'Link successfully attached to page', - /** - * Profile View - */ + // Profile View 'profile_user_for_x' => 'User for :time', 'profile_created_content' => 'Created Content', 'profile_not_created_pages' => ':userName has not created any pages', 'profile_not_created_chapters' => ':userName has not created any chapters', 'profile_not_created_books' => ':userName has not created any books', - /** - * Comments - */ + // Comments 'comment' => 'Comment', 'comments' => 'Comments', 'comment_add' => 'Add Comment', @@ -302,9 +286,7 @@ return [ 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', 'comment_in_reply_to' => 'In reply to :commentId', - /** - * Revision - */ + // Revision 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', 'revision_delete_success' => 'Revision deleted', 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.' diff --git a/resources/lang/en/errors.php b/resources/lang/en/errors.php index fb09841cf..eb73b880b 100644 --- a/resources/lang/en/errors.php +++ b/resources/lang/en/errors.php @@ -1,11 +1,9 @@ 'You do not have permission to access the requested page.', 'permissionJson' => 'You do not have permission to perform the requested action.', @@ -80,4 +78,5 @@ return [ 'error_occurred' => 'An Error Occurred', 'app_down' => ':appName is down right now', 'back_soon' => 'It will be back up soon.', + ]; diff --git a/resources/lang/en/pagination.php b/resources/lang/en/pagination.php index fcab34b25..85bd12fc3 100644 --- a/resources/lang/en/pagination.php +++ b/resources/lang/en/pagination.php @@ -1,18 +1,11 @@ '« Previous', 'next' => 'Next »', diff --git a/resources/lang/en/passwords.php b/resources/lang/en/passwords.php index 7c10cba1a..9f7d9e3cb 100644 --- a/resources/lang/en/passwords.php +++ b/resources/lang/en/passwords.php @@ -1,18 +1,11 @@ 'Passwords must be at least six characters and match the confirmation.', 'user' => "We can't find a user with that e-mail address.", 'token' => 'This password reset token is invalid.', diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php index f5bd8e27d..6e48c517d 100755 --- a/resources/lang/en/settings.php +++ b/resources/lang/en/settings.php @@ -1,21 +1,17 @@ 'Settings', 'settings_save' => 'Save Settings', 'settings_save_success' => 'Settings saved', - /** - * App settings - */ - + // App Settings 'app_settings' => 'App Settings', 'app_name' => 'Application name', 'app_name_desc' => 'This name is shown in the header and any emails.', @@ -37,10 +33,7 @@ return [ 'app_disable_comments' => 'Disable comments', 'app_disable_comments_desc' => 'Disable comments across all pages in the application. Existing comments are not shown.', - /** - * Registration settings - */ - + // Registration Settings 'reg_settings' => 'Registration Settings', 'reg_allow' => 'Allow registration?', 'reg_default_role' => 'Default user role after registration', @@ -50,10 +43,7 @@ return [ 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', - /** - * Maintenance settings - */ - + // Maintenance settings 'maint' => 'Maintenance', 'maint_image_cleanup' => 'Cleanup Images', 'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.", @@ -63,10 +53,7 @@ return [ 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', 'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!', - /** - * Role settings - */ - + // Role Settings 'roles' => 'Roles', 'role_user_roles' => 'User Roles', 'role_create' => 'Create New Role', @@ -99,10 +86,7 @@ return [ 'role_users' => 'Users in this role', 'role_users_none' => 'No users are currently assigned to this role', - /** - * Users - */ - + // Users 'users' => 'Users', 'user_profile' => 'User Profile', 'users_add_new' => 'Add New User', diff --git a/resources/lang/format.php b/resources/lang/format.php index 8698d1bb7..6815268c6 100755 --- a/resources/lang/format.php +++ b/resources/lang/format.php @@ -4,119 +4,150 @@ /** * Format a language file in the same way as the EN equivalent. * Matches the line numbers of translated content. + * Potentially destructive, Ensure you have a backup of your translation content before running. */ $args = array_slice($argv, 1); if (count($args) < 2) { - errorOut("Please provide a language code as the first argument and a translation file name as the second (./format.php fr activities)"); + errorOut("Please provide a language code as the first argument and a translation file name, or '--all', as the second (./format.php fr activities)"); } -$lang = formatLang($args[0]); +$lang = formatLocale($args[0]); $fileName = explode('.', $args[1])[0]; +$filesNames = [$fileName]; +if ($fileName === '--all') { + $fileNames = getTranslationFileNames(); +} -$enLines = loadLangFileLines('en', $fileName); -$langContent = loadLang($lang, $fileName); -$enContent = loadLang('en', $fileName); +foreach ($fileNames as $fileName) { + $formatted = formatFileContents($lang, $fileName); + writeLangFile($lang, $fileName, $formatted); +} -// Calculate the longest top-level key length -$longestKeyLength = longestKey($enContent); -// Start formatted content -$formatted = []; -$mode = 'header'; -$arrayKeys = []; +/** + * Format the contents of a single translation file in the given language. + * @param string $lang + * @param string $fileName + * @return string + */ +function formatFileContents(string $lang, string $fileName) : string { + $enLines = loadLangFileLines('en', $fileName); + $langContent = loadLang($lang, $fileName); + $enContent = loadLang('en', $fileName); -foreach($enLines as $index => $line) { - $trimLine = trim($line); - if ($mode === 'header') { - $formatted[$index] = $line; - if (str_replace(' ', '', $trimLine) === 'return[') $mode = 'body'; - } + // Calculate the longest top-level key length + $longestKeyLength = calculateKeyPadding($enContent); - if ($mode === 'body') { - $matches = []; + // Start formatted content + $formatted = []; + $mode = 'header'; + $arrayKeys = []; - // Comment - if (strpos($trimLine, '//') === 0) { - $formatted[$index] = "\t" . $trimLine; - continue; + foreach($enLines as $index => $line) { + $trimLine = trim($line); + if ($mode === 'header') { + $formatted[$index] = $line; + if (str_replace(' ', '', $trimLine) === 'return[') $mode = 'body'; } - // Arrays - $arrayStartMatch = preg_match('/^\'(.*)\'\s+?=>\s+?\[(\],)?\s*?$/', $trimLine, $matches); - $arrayEndMatch = preg_match('/]\s*,\s*$/', $trimLine); - $indent = count($arrayKeys) + 1; - if ($arrayStartMatch === 1) { - $arrayKeys[] = $matches[1]; - $formatted[$index] = str_repeat(" ", $indent * 4) . str_pad("'{$matches[1]}'", $longestKeyLength) . "=> ["; - if ($arrayEndMatch !== 1) continue; - } - if ($arrayEndMatch === 1) { - unsetArrayByKeys($langContent, $arrayKeys); - $key = array_pop($arrayKeys); - if (isset($formatted[$index])) { - $formatted[$index] .= '],'; - } else { - $formatted[$index] = str_repeat(" ", ($indent-1) * 4) . "],"; - } - continue; - } + if ($mode === 'body') { + $matches = []; - // Translation - $translationMatch = preg_match('/^\'(.*)\'\s+?=>\s+?\'(.*)?\'.+?$/', $trimLine, $matches); - if ($translationMatch === 1) { - $key = $matches[1]; - $keys = array_merge($arrayKeys, [$key]); - $langVal = getTranslationByKeys($langContent, $keys); - if (empty($langVal)) continue; - - $keyPad = $longestKeyLength; - if (count($arrayKeys) === 0) { - unset($langContent[$key]); - } else { - $keyPad = longestKey(getTranslationByKeys($enContent, $arrayKeys)); + // Comment + if (strpos($trimLine, '//') === 0) { + $formatted[$index] = "\t" . $trimLine; + continue; } - $formatted[$index] = formatTranslationLine($key, $langVal, $indent, $keyPad); - continue; + // Arrays + $arrayStartMatch = preg_match('/^\'(.*)\'\s+?=>\s+?\[(\],)?\s*?$/', $trimLine, $matches); + $arrayEndMatch = preg_match('/]\s*,\s*$/', $trimLine); + $indent = count($arrayKeys) + 1; + if ($arrayStartMatch === 1) { + $arrayKeys[] = $matches[1]; + $formatted[$index] = str_repeat(" ", $indent * 4) . str_pad("'{$matches[1]}'", $longestKeyLength) . "=> ["; + if ($arrayEndMatch !== 1) continue; + } + if ($arrayEndMatch === 1) { + unsetArrayByKeys($langContent, $arrayKeys); + array_pop($arrayKeys); + if (isset($formatted[$index])) { + $formatted[$index] .= '],'; + } else { + $formatted[$index] = str_repeat(" ", ($indent-1) * 4) . "],"; + } + continue; + } + + // Translation + $translationMatch = preg_match('/^\'(.*)\'\s+?=>\s+?\'(.*)?\'.+?$/', $trimLine, $matches); + if ($translationMatch === 1) { + $key = $matches[1]; + $keys = array_merge($arrayKeys, [$key]); + $langVal = getTranslationByKeys($langContent, $keys); + if (empty($langVal)) continue; + + $keyPad = $longestKeyLength; + if (count($arrayKeys) === 0) { + unset($langContent[$key]); + } else { + $keyPad = calculateKeyPadding(getTranslationByKeys($enContent, $arrayKeys)); + } + + $formatted[$index] = formatTranslationLine($key, $langVal, $indent, $keyPad); + continue; + } + } + + } + + // Fill missing lines + $arraySize = max(array_keys($formatted)); + $formatted = array_replace(array_fill(0, $arraySize, ''), $formatted); + + // Add remaining translations + $langContent = array_filter($langContent, function($item) { + return !is_null($item) && !empty($item); + }); + if (count($langContent) > 0) { + $formatted[] = ''; + $formatted[] = "\t// Unmatched"; + } + foreach ($langContent as $key => $value) { + if (is_array($value)) { + $formatted[] = formatTranslationArray($key, $value); + } else { + $formatted[] = formatTranslationLine($key, $value); } } + // Add end line + $formatted[] = '];'; + return implode("\n", $formatted); } -// Fill missing lines -$arraySize = max(array_keys($formatted)); -$formatted = array_replace(array_fill(0, $arraySize, ''), $formatted); - -// Add remaining translations -$langContent = array_filter($langContent, function($item) { - return !is_null($item) && !empty($item); -}); -if (count($langContent) > 0) { - $formatted[] = ''; - $formatted[] = "\t// Unmatched"; -} -foreach ($langContent as $key => $value) { - if (is_array($value)) { - $formatted[] = formatTranslationArray($key, $value); - } else { - $formatted[] = formatTranslationLine($key, $value); - } -} - -// Add end line -$formatted[] = '];'; -$formatted = implode("\n", $formatted); - -writeLangFile($lang, $fileName, $formatted); - -function formatTranslationLine(string $key, string $value, int $indent = 1, int $keyPad = 1) { +/** + * Format a translation line. + * @param string $key + * @param string $value + * @param int $indent + * @param int $keyPad + * @return string + */ +function formatTranslationLine(string $key, string $value, int $indent = 1, int $keyPad = 1) : string { $escapedValue = str_replace("'", "\\'", $value); return str_repeat(" ", $indent * 4) . str_pad("'{$key}'", $keyPad, ' ') ."=> '{$escapedValue}',"; } -function longestKey(array $array) { +/** + * Find the longest key in the array and provide the length + * for all keys to be used when printed. + * @param array $array + * @return int + */ +function calculateKeyPadding(array $array) : int { $top = 0; foreach ($array as $key => $value) { $keyLen = strlen($key); @@ -125,12 +156,27 @@ function longestKey(array $array) { return $top + 3; } -function formatTranslationArray(string $key, array $array) { +/** + * Format an translation array with the given key. + * Simply prints as an old-school php array. + * Used as a last-resort backup to save unused translations. + * @param string $key + * @param array $array + * @return string + */ +function formatTranslationArray(string $key, array $array) : string { $arrayPHP = var_export($array, true); return " '{$key}' => {$arrayPHP},"; } -function getTranslationByKeys(array $translations, array $keys) { +/** + * Find a string translation value within a multi-dimensional array + * by traversing the given array of keys. + * @param array $translations + * @param array $keys + * @return string|array + */ +function getTranslationByKeys(array $translations, array $keys) { $val = $translations; foreach ($keys as $key) { $val = $val[$key] ?? ''; @@ -139,6 +185,12 @@ function getTranslationByKeys(array $translations, array $keys) { return $val; } +/** + * Unset an inner item of a multi-dimensional array by + * traversing the given array of keys. + * @param array $input + * @param array $keys + */ function unsetArrayByKeys(array &$input, array $keys) { $val = &$input; $lastIndex = count($keys) - 1; @@ -151,6 +203,12 @@ function unsetArrayByKeys(array &$input, array $keys) { } } +/** + * Write the given content to a translation file. + * @param string $lang + * @param string $fileName + * @param string $content + */ function writeLangFile(string $lang, string $fileName, string $content) { $path = __DIR__ . "/{$lang}/{$fileName}.php"; if (!file_exists($path)) { @@ -159,7 +217,13 @@ function writeLangFile(string $lang, string $fileName, string $content) { file_put_contents($path, $content); } -function loadLangFileLines(string $lang, string $fileName) { +/** + * Load the contents of a language file as an array of text lines. + * @param string $lang + * @param string $fileName + * @return array + */ +function loadLangFileLines(string $lang, string $fileName) : array { $path = __DIR__ . "/{$lang}/{$fileName}.php"; if (!file_exists($path)) { errorOut("Expected translation file '{$path}' does not exist"); @@ -170,7 +234,13 @@ function loadLangFileLines(string $lang, string $fileName) { }, $lines); } -function loadLang(string $lang, string $fileName) { +/** + * Load the contents of a language file + * @param string $lang + * @param string $fileName + * @return array + */ +function loadLang(string $lang, string $fileName) : array { $path = __DIR__ . "/{$lang}/{$fileName}.php"; if (!file_exists($path)) { errorOut("Expected translation file '{$path}' does not exist"); @@ -180,21 +250,57 @@ function loadLang(string $lang, string $fileName) { return $fileData; } -function formatLang($lang) { +/** + * Fetch an array containing the names of all translation files without the extension. + * @return array + */ +function getTranslationFileNames() : array { + $dir = __DIR__ . "/en"; + if (!file_exists($dir)) { + errorOut("Expected directory '{$dir}' does not exist"); + } + $files = scandir($dir); + $fileNames = []; + foreach ($files as $file) { + if (substr($file, -4) === '.php') { + $fileNames[] = substr($file, 0, strlen($file) - 4); + } + } + return $fileNames; +} + +/** + * Format a locale to follow the lowercase_UPERCASE standard + * @param string $lang + * @return string + */ +function formatLocale(string $lang) : string { $langParts = explode('_', strtoupper($lang)); $langParts[0] = strtolower($langParts[0]); return implode('_', $langParts); } +/** + * Dump a variable then die. + * @param $content + */ function dd($content) { print_r($content); exit(1); } +/** + * Log out some information text in blue + * @param $text + */ function info($text) { echo "\e[34m" . $text . "\e[0m\n"; } +/** + * Log out an error in red and exit. + * @param $text + */ function errorOut($text) { echo "\e[31m" . $text . "\e[0m\n"; exit(1);