WebOrbiton
v1.0.0.8

Publisium

205 lines · 10.2 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/languages.php';
  6. ​
  7. final class Language
  8. {
  9. private const FILE_PATH = __DIR__ . '/../translations/language.php';
  10. ​
  11. private static array $merged = [];
  12. ​
  13. public static function base(): array
  14. {
  15. if (!is_file(self::FILE_PATH)) {
  16. return [];
  17. }
  18. ​
  19. $strings = require self::FILE_PATH;
  20. return is_array($strings) ? $strings : [];
  21. }
  22. ​
  23. public static function all(): array
  24. {
  25. $code = Languages::current();
  26. if ($code === '') {
  27. return self::base();
  28. }
  29. ​
  30. if (!isset(self::$merged[$code])) {
  31. $translated = array_filter(Languages::strings($code), static fn($value): bool => is_string($value) && $value !== '');
  32. self::$merged[$code] = array_merge(self::base(), $translated);
  33. }
  34. ​
  35. return self::$merged[$code];
  36. }
  37. ​
  38. public static function get(string $key, string $default = ''): string
  39. {
  40. $strings = self::all();
  41. return (string) ($strings[$key] ?? $default);
  42. }
  43. ​
  44. public const ADDED_DEFAULTS = [
  45. 'language_switcher_label' => 'Language',
  46. 'article_toc_title' => 'Table of contents',
  47. 'back_to_top' => 'Back to top',
  48. 'reading_progress_left' => '{percent}% left',
  49. 'breadcrumb_label' => 'Breadcrumb',
  50. 'article_written_by' => 'Written by',
  51. 'author_articles_count' => 'Articles: {count}',
  52. 'artist_articles_title' => 'Articles by {name}',
  53. 'artist_not_found' => 'Author not found.',
  54. 'artist_website' => 'Website',
  55. 'artist_no_articles' => 'No published articles yet.',
  56. 'article_tags_label' => 'Tags:',
  57. 'tag_page_title' => 'Tagged: {name}',
  58. 'tag_not_found' => 'Tag not found.',
  59. 'tag_no_articles' => 'No published articles with this tag yet.',
  60. 'read_aloud_title' => 'Listen to this article',
  61. 'read_aloud_play' => 'Play',
  62. 'read_aloud_pause' => 'Pause',
  63. 'read_aloud_seek' => 'Seek',
  64. 'read_aloud_speed' => 'Playback speed',
  65. 'article_subscribers_only' => 'Subscribers only',
  66. 'article_subscribe_button' => 'Subscribe',
  67. 'article_login_button' => 'Log in',
  68. 'article_already_subscriber' => 'Already a subscriber?',
  69. 'article_buy_lifetime_button' => 'Get lifetime access',
  70. 'subscription_one_time' => 'one-time payment',
  71. 'subscription_per_month' => 'month',
  72. 'subscription_per_year' => 'year',
  73. 'contact_name' => 'Your name',
  74. 'contact_email' => 'Your email',
  75. 'contact_subject' => 'Subject',
  76. 'contact_topic' => 'Topic',
  77. 'contact_topic_choose' => 'Choose a topic',
  78. 'contact_topic_missing' => 'Please choose a topic.',
  79. 'contact_topic_other' => 'Other…',
  80. 'contact_subject_other' => 'Your subject',
  81. 'contact_subject_other_missing' => 'Please enter your subject.',
  82. 'contact_message' => 'Message',
  83. 'contact_security_code' => 'Security code',
  84. 'contact_send' => 'Send message',
  85. 'contact_sent' => 'Thank you! Your message has been sent.',
  86. 'contact_unavailable' => 'The contact form is not available right now.',
  87. 'contact_security_failed' => 'The security code was wrong. Please try again.',
  88. 'contact_invalid' => 'Please enter your name, a valid email address and a message.',
  89. 'contact_rate_limited' => 'You have sent too many messages. Please try again later.',
  90. 'contact_send_failed' => 'Your message could not be sent. Please try again later.',
  91. 'auth_session_expired' => 'Your session expired. Please try again.',
  92. 'account_new_password' => 'New password (leave empty to keep your current one)',
  93. 'account_no_subscriptions' => 'You don’t have any subscriptions yet.',
  94. 'account_subscription_until' => 'until {date}',
  95. 'account_subscription_email_note' => 'Pay with {email} so the subscription is linked to this account.',
  96. 'account_consent_analytics' => 'Allow analytics, so we can see which articles people read',
  97. 'account_consent_ads' => 'Allow personalized ads',
  98. 'not_found_title' => 'Not found',
  99. 'article_not_found' => 'We couldn’t find this article. It may have been moved or deleted.',
  100. 'comments_coming_soon' => 'Comments are coming soon.',
  101. 'category_not_found' => 'We couldn’t find this category.',
  102. 'category_no_articles' => 'No articles in this category yet.',
  103. 'category_rss' => 'RSS feed for this category',
  104. 'account_password_too_short' => 'Your new password needs at least 8 characters.',
  105. 'account_profile_saved' => 'Your profile is saved.',
  106. 'account_preferences_saved' => 'Your choices are saved.',
  107. 'antibot_image_label' => 'Security code to type in',
  108. 'theme_toggle' => 'Switch between light and dark mode',
  109. 'consent_text' => 'We use cookies and similar technologies to improve your experience.',
  110. 'nav_menu_toggle' => 'Open menu',
  111. 'pagination_label' => 'Pages',
  112. 'auth_registration_closed' => 'Sign-ups are closed at the moment.',
  113. 'auth_invalid_email' => 'Please enter a valid email address.',
  114. 'auth_password_too_short' => 'Your password needs at least 8 characters.',
  115. 'auth_email_taken' => 'There’s already an account with this email. Try logging in instead.',
  116. 'home_no_articles' => 'No articles yet. Check back soon.',
  117. 'page_not_found' => 'We couldn’t find this page. It may have been moved or deleted.',
  118. 'auth_login_disabled' => 'Reader login is temporarily disabled. Please check back later.',
  119. 'auth_security_code_wrong' => 'The security code was wrong. Please try again.',
  120. 'auth_enter_email_password' => 'Enter your email and password.',
  121. 'auth_too_many_attempts' => 'Too many wrong tries. Please wait about {minutes} min and try again.',
  122. 'auth_wrong_credentials' => 'That email or password doesn’t match. Try again.',
  123. 'auth_passwords_mismatch' => 'The passwords don’t match.',
  124. 'auth_reader_account' => 'Reader account',
  125. 'auth_security_code' => 'Security code',
  126. 'subscription_status_active' => 'Active',
  127. 'subscription_status_trialing' => 'Free trial',
  128. 'subscription_status_past_due' => 'Payment overdue',
  129. 'subscription_status_canceled' => 'Canceled',
  130. 'subscription_status_expired' => 'Expired',
  131. 'ad_close' => 'Close ad',
  132. 'adblock_title' => 'Looks like you’re using an ad blocker',
  133. 'adblock_text' => 'Ads help us keep this site free for everyone. Please turn off your ad blocker for this site, or add it to your allow list.',
  134. 'adblock_reload' => 'I’ve turned it off, reload the page',
  135. 'adblock_dismiss' => 'Continue anyway',
  136. 'adblock_subscribe' => 'Or read without ads with a subscription',
  137. 'consent_manage_button' => 'Manage',
  138. 'consent_save' => 'Save choices',
  139. 'consent_necessary' => 'Necessary',
  140. 'consent_necessary_desc' => 'Needed for the site to work, for example to remember these choices. Always on.',
  141. 'consent_analytics' => 'Analytics',
  142. 'consent_analytics_desc' => 'Helps us see which articles people read, so we can make the site better.',
  143. 'consent_marketing' => 'Marketing',
  144. 'consent_marketing_desc' => 'Lets advertising partners show ads that fit your interests.',
  145. 'comments_consent_text' => 'Comments come from an outside service that uses cookies. Allow them in the cookie settings to read and write comments.',
  146. 'comments_consent_button' => 'Cookie settings',
  147. 'comments_load_note' => 'Comments are loaded from an outside service only when you ask for them.',
  148. 'comments_load_button' => 'Show comments',
  149. 'site_comments_empty' => 'No comments yet. Be the first to share your thoughts.',
  150. 'site_comments_reply' => 'Reply',
  151. 'site_comments_replying_to' => 'Replying to',
  152. 'site_comments_cancel_reply' => 'Cancel',
  153. 'site_comments_as' => 'Commenting as',
  154. 'site_comments_name' => 'Your name',
  155. 'site_comments_body' => 'Your comment',
  156. 'site_comments_security_code' => 'Security code',
  157. 'site_comments_privacy' => 'Only your name and your comment are saved. No email address is needed, and your IP address is not stored with the comment.',
  158. 'site_comments_submit' => 'Post comment',
  159. 'site_comments_login_required' => 'Log in to join the discussion.',
  160. 'site_comments_login' => 'Log in',
  161. 'site_comments_closed' => 'Comments are closed for this article.',
  162. 'site_comments_security_failed' => 'The security code was wrong or the form expired. Please try again.',
  163. 'site_comments_invalid' => 'Please enter your name and a comment.',
  164. 'site_comments_rate_limited' => 'You are posting too fast. Please wait a few minutes and try again.',
  165. 'site_comments_pending' => 'Thanks! Your comment will appear after a moderator approves it.',
  166. 'site_comments_published' => 'Thanks! Your comment is published.',
  167. 'site_comments_email' => 'Your email',
  168. 'site_comments_email_invalid' => 'Please enter a valid email address.',
  169. 'site_comments_privacy_email' => 'Your name and your comment are shown publicly. Your email address is only visible to the site team and is never published. Your IP address is not stored with the comment.',
  170. ];
  171. ​
  172. public static function ensureKeys(array $defaults): void
  173. {
  174. $current = self::base();
  175. $missing = array_diff_key($defaults, $current);
  176. ​
  177. if (empty($current) || empty($missing)) {
  178. return;
  179. }
  180. ​
  181. self::save($current + $missing);
  182. }
  183. ​
  184. public static function save(array $strings): bool
  185. {
  186. self::$merged = [];
  187. ​
  188. $clean = [];
  189. foreach ($strings as $key => $value) {
  190. if (!is_string($key)) {
  191. continue;
  192. }
  193. $clean[$key] = strip_tags((string) $value);
  194. }
  195. ​
  196. $export = "<?php\n\ndeclare(strict_types=1);\n\nreturn [\n";
  197. foreach ($clean as $key => $value) {
  198. $export .= " " . var_export($key, true) . " => " . var_export($value, true) . ",\n";
  199. }
  200. $export .= "];\n";
  201. ​
  202. return file_put_contents(self::FILE_PATH, $export) !== false;
  203. }
  204. }
  205. ​