the browser-facing portion of osu!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'master' into feature/discussion-post-markdown

bakaneko 12ecd7ea 58eef703

+2283 -1600
+2 -2
.env.dusk.local.example
··· 1 1 APP_KEY= 2 2 APP_ENV=testing 3 3 4 - APP_URL=http://nginx 5 - NOTIFICATION_ENDPOINT=/home/notifications/feed-dusk 4 + APP_URL=http://localhost:8000 5 + NOTIFICATION_ENDPOINT=ws://notification-server-dusk:2345 6 6 7 7 DB_DATABASE=osu_test 8 8 DB_DATABASE_CHAT=osu_chat_test
+9 -1
.env.example
··· 251 251 252 252 # OAUTH_MAX_USER_CLIENTS=1 253 253 254 + # USER_REPORT_NOTIFICATION_ENDPOINT_CHEATING= 255 + # default if nothing specified for specific type 254 256 # USER_REPORT_NOTIFICATION_ENDPOINT_MODERATION= 255 - # USER_REPORT_NOTIFICATION_ENDPOINT_CHEATING= 257 + 258 + # USER_REPORT_NOTIFICATION_ENDPOINT_BEATMAPSET= 259 + # USER_REPORT_NOTIFICATION_ENDPOINT_BEATMAPSET_DISCUSSION= 260 + # USER_REPORT_NOTIFICATION_ENDPOINT_CHAT= 261 + # USER_REPORT_NOTIFICATION_ENDPOINT_COMMENT= 262 + # USER_REPORT_NOTIFICATION_ENDPOINT_FORUM= 263 + # USER_REPORT_NOTIFICATION_ENDPOINT_USER= 256 264 257 265 # LOG_CHANNEL=single 258 266
+3 -1
.env.testing.example
··· 1 - DB_DATABASE=osu_test 2 1 DB_DATABASE_CHAT=osu_chat_test 3 2 DB_DATABASE_MP=osu_mp_test 4 3 DB_DATABASE_STORE=osu_store_test 5 4 DB_DATABASE_UPDATES=osu_updates_test 6 5 DB_DATABASE_CHARTS=osu_charts_test 7 6 7 + # match with docker-compose.yml 8 + DB_DATABASE=osu_test 8 9 ES_INDEX_PREFIX=test_ 10 + SCHEMA=test 9 11 10 12 PAYMENT_SANDBOX=true
+4 -4
.github/workflows/tests.yml
··· 152 152 run: ./bin/phpunit.sh 153 153 154 154 # TODO: workaround things (beatmaps) being indexed during test above and not cleaned up. 155 - # This results in: 156 - # 1. ranked beatmapset with null approved date being indexed 157 - # 2. ES then returns Long.MIN_VALUE for null dates cursor 158 - # 3. and finally ES parser fails when parsing the Long.MIN_VALUE back when passed as cursor 155 + # This used to cause beatmap listing returning cursor with Long.MIN_VALUE for null timetamp 156 + # and errors out when trying to get the next page (es can't parse such value for timestamp) 157 + # but has since been fixed. 158 + # Something should still be done regarding es index between tests though. 159 159 - name: Clean indexes 160 160 run: php artisan es:index-documents --yes 161 161
+1 -1
README.md
··· 27 27 28 28 While we have standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible. 29 29 30 - For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. 30 + We love to reward quality contributions. If you have made a large contribution or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so. 31 31 32 32 ## Seeking Help 33 33
+1
SETUP.md
··· 319 319 or if using Docker: 320 320 321 321 ``` 322 + # `compose exec` doesn't work here due to port conflict with dev instance 322 323 docker compose run --rm php test browser 323 324 ``` 324 325
+1 -1
app/Http/Controllers/Forum/PostsController.php
··· 156 156 $text = $post->bodyRaw; 157 157 158 158 if (Request::input('quote') === '1') { 159 - $text = sprintf('[quote="%s"]%s[/quote]', $post->userNormalized()->username, $text); 159 + $text = sprintf("[quote=\"%s\"]\n%s\n[/quote]\n", $post->userNormalized()->username, $text); 160 160 } 161 161 162 162 return $text;
+1 -1
app/Http/Middleware/SetLocale.php
··· 41 41 42 42 App::setLocale($locale); 43 43 // Carbon setLocale normalizes the locale 44 - Carbon::setLocale($locale); 44 + Carbon::setLocale($locale === 'sr' ? 'sr_Cyrl' : $locale); 45 45 } 46 46 47 47 private function localeFromHeader(Request $request): string
+46 -24
app/Libraries/BBCodeForDB.php
··· 10 10 11 11 class BBCodeForDB 12 12 { 13 + const EXTRA_ESCAPES = [ 14 + '[' => '&#91;', 15 + ']' => '&#93;', 16 + '.' => '&#46;', 17 + ':' => '&#58;', 18 + "\n" => '&#10;', 19 + '@' => '&#64;', 20 + ]; 21 + 13 22 public $text; 14 23 public $uid; 15 24 ··· 20 29 21 30 public function extraEscapes($text) 22 31 { 23 - return strtr( 24 - $text, 25 - [ 26 - '[' => '&#91;', 27 - ']' => '&#93;', 28 - '.' => '&#46;', 29 - ':' => '&#58;', 30 - "\n" => '&#10;', 31 - '@' => '&#64;', 32 - ], 33 - ); 32 + return strtr($text, static::EXTRA_ESCAPES); 33 + } 34 + 35 + public static function extraUnescape(string $text): string 36 + { 37 + static $mapping; 38 + $mapping ??= array_flip(static::EXTRA_ESCAPES); 39 + 40 + return strtr($text, $mapping); 34 41 } 35 42 36 43 public function __construct($text = '') ··· 72 79 73 80 public function parseBox($text) 74 81 { 75 - $text = preg_replace('#\[box=([^]]*?)\]#s', "[box=\\1:{$this->uid}]", $text); 76 - $text = str_replace('[/box]', "[/box:{$this->uid}]", $text); 77 - $text = str_replace('[spoilerbox]', "[spoilerbox:{$this->uid}]", $text); 78 - $text = str_replace('[/spoilerbox]', "[/spoilerbox:{$this->uid}]", $text); 82 + $text = preg_replace('#\[box=((\\\[\[\]]|[^][]|\[(\\\[\[\]]|[^][]|(?R))*\])*?)\]#s', "[box=\\1:{$this->uid}]", $text); 79 83 80 - return $text; 84 + return strtr($text, [ 85 + '[/box]' => "[/box:{$this->uid}]", 86 + '[spoilerbox]' => "[spoilerbox:{$this->uid}]", 87 + '[/spoilerbox]' => "[/spoilerbox:{$this->uid}]", 88 + ]); 81 89 } 82 90 83 91 public function parseCode($text) ··· 134 142 return $text; 135 143 } 136 144 145 + public function parseImagemap($text) 146 + { 147 + return preg_replace_callback( 148 + '#\[imagemap\](.+?)\[/imagemap\]#s', 149 + function ($m) { 150 + $escapedMap = $this->extraEscapes($m[1]); 151 + 152 + return "[imagemap]{$escapedMap}[/imagemap]"; 153 + }, 154 + $text 155 + ); 156 + } 157 + 137 158 /** 138 159 * Handles: 139 160 * - Code (c) ··· 159 180 160 181 // internal url 161 182 $text = preg_replace( 162 - "#{$spaces[0]}({$internalUrl}/([^\s]+?)){$spaces[1]}#", 163 - "\\1<!-- m --><a href='\\2' rel='nofollow'>\\3</a><!-- m -->\\4", 183 + "#{$spaces[0]}({$internalUrl}/([^\s]+?))(?={$spaces[1]})#", 184 + "\\1<!-- m --><a href='\\2' rel='nofollow'>\\3</a><!-- m -->", 164 185 $text 165 186 ); 166 187 167 188 // plain http/https/ftp 168 189 $text = preg_replace( 169 - "#{$spaces[0]}((?:https?|ftp)://[^\s]+?){$spaces[1]}#", 170 - "\\1<!-- m --><a href='\\2' rel='nofollow'>\\2</a><!-- m -->\\3", 190 + "#{$spaces[0]}((?:https?|ftp)://[^\s]+?)(?={$spaces[1]})#", 191 + "\\1<!-- m --><a href='\\2' rel='nofollow'>\\2</a><!-- m -->", 171 192 $text 172 193 ); 173 194 174 195 // www 175 196 $text = preg_replace( 176 - "#{$spaces[0]}(www\.[^\s]+){$spaces[1]}#", 177 - "\\1<!-- w --><a href='http://\\2' rel='nofollow'>\\2</a><!-- w -->\\3", 197 + "#{$spaces[0]}(www\.[^\s]+)(?={$spaces[1]})#", 198 + "\\1<!-- w --><a href='http://\\2' rel='nofollow'>\\2</a><!-- w -->", 178 199 $text 179 200 ); 180 201 181 202 // emails 182 203 $text = preg_replace( 183 - "#{$spaces[0]}([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z-]+){$spaces[1]}#", 184 - "\\1<!-- e --><a href='mailto:\\2' rel='nofollow'>\\2</a><!-- e -->\\3", 204 + "#{$spaces[0]}([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z-]+)(?={$spaces[1]})#", 205 + "\\1<!-- e --><a href='mailto:\\2' rel='nofollow'>\\2</a><!-- e -->", 185 206 $text 186 207 ); 187 208 ··· 369 390 $text = htmlentities($this->text, ENT_QUOTES, 'UTF-8', true); 370 391 371 392 $text = $this->unifyNewline($text); 393 + $text = $this->parseImagemap($text); 372 394 $text = $this->parseCode($text); 373 395 $text = $this->parseNotice($text); 374 396 $text = $this->parseBox($text);
+58 -14
app/Libraries/BBCodeFromDB.php
··· 38 38 } 39 39 } 40 40 41 - public function clearSpacesBetweenTags($text) 42 - { 43 - return preg_replace('/([^-][^-]>)\s*</', '\1<', $text); 44 - } 45 - 46 41 public function parseAudio($text) 47 42 { 48 43 preg_match_all("#\[audio:{$this->uid}\](?<url>[^[]+)\[/audio:{$this->uid}\]#", $text, $matches, PREG_SET_ORDER); ··· 67 62 68 63 public function parseBox($text) 69 64 { 70 - $text = preg_replace("#\[box=([^]]*?):{$this->uid}\]\n*#s", $this->parseBoxHelperPrefix('\\1'), $text); 65 + $text = preg_replace("#\[box=((\\\[\[\]]|[^][]|\[(\\\[\[\]]|[^][]|(?R))*\])*?):{$this->uid}\]\n*#s", $this->parseBoxHelperPrefix('\\1'), $text); 71 66 $text = preg_replace("#\n*\[/box:{$this->uid}]\n?#s", $this->parseBoxHelperSuffix(), $text); 72 67 73 68 $text = preg_replace("#\[spoilerbox:{$this->uid}\]\n*#s", $this->parseBoxHelperPrefix(), $text); ··· 129 124 public function parseHeading($text) 130 125 { 131 126 $text = str_replace("[heading:{$this->uid}]", '<h2>', $text); 132 - $text = str_replace("[/heading:{$this->uid}]", '</h2>', $text); 127 + $text = preg_replace("#\[/heading:{$this->uid}\]\n?#", '</h2>', $text); 133 128 134 129 return $text; 135 130 } 136 131 132 + public function parseImagemap($text) 133 + { 134 + return preg_replace_callback( 135 + '#(\[imagemap\].+?\[/imagemap\]\n?)#', 136 + function ($m) { 137 + return preg_replace_callback( 138 + '#\[imagemap\]\n(?<imageUrl>https?://.+)\n(?<links>(?:(?:[0-9.]+ ){4}(?:\#|https?://[^ ]+|mailto:[^ ]+)(?: .+)?\n)+)\[/imagemap\]\n?#', 139 + function ($map) { 140 + $links = array_map( 141 + fn ($rawLink) => explode(' ', $rawLink, 6), 142 + explode("\n", $map['links']), 143 + ); 144 + array_pop($links); // remove the empty string from last newline 145 + 146 + $linksHtml = implode('', array_map( 147 + fn ($link) => tag($link[4] === '#' ? 'span' : 'a', [ 148 + 'class' => 'imagemap__link', 149 + 'href' => $link[4], 150 + 'style' => implode(';', [ 151 + "left: {$link[0]}%", 152 + "top: {$link[1]}%", 153 + "width: {$link[2]}%", 154 + "height: {$link[3]}%", 155 + ]), 156 + 'title' => $link[5] ?? '', 157 + ]), 158 + $links, 159 + )); 160 + 161 + $imageUrl = proxy_media($map['imageUrl']); 162 + $imageAttributes = [ 163 + 'class' => 'imagemap__image', 164 + 'loading' => 'lazy', 165 + 'src' => $imageUrl, 166 + ]; 167 + $imageSize = fast_imagesize($imageUrl); 168 + if ($imageSize !== null) { 169 + $imageAttributes['width'] = $imageSize[0]; 170 + $imageAttributes['height'] = $imageSize[1]; 171 + } 172 + $imageHtml = tag('img', $imageAttributes); 173 + 174 + return tag('div', ['class' => 'imagemap'], $imageHtml.$linksHtml); 175 + }, 176 + html_entity_decode_better(BBCodeForDB::extraUnescape($m[1])), 177 + ); 178 + }, 179 + $text, 180 + ); 181 + } 182 + 137 183 public function parseItalic($text) 138 184 { 139 185 $text = str_replace("[i:{$this->uid}]", '<em>', $text); ··· 195 241 $text = preg_replace("#\[list:{$this->uid}\]\s*\[\*:{$this->uid}\]#", '<ol class="unordered"><li>', $text); 196 242 197 243 // convert list items. 198 - $text = preg_replace("#\[/\*(:m)?:{$this->uid}\]\n?#", '</li>', $text); 199 - $text = str_replace("[*:{$this->uid}]", '<li>', $text); 244 + $text = preg_replace("#\[/\*(:m)?:{$this->uid}\]\n?\n?#", '</li>', $text); 245 + $text = preg_replace("#\s*\[\*:{$this->uid}\]#", '<li>', $text); 200 246 201 247 // close list tags. 202 - $text = str_replace("[/list:o:{$this->uid}]", '</ol>', $text); 203 - $text = str_replace("[/list:u:{$this->uid}]", '</ol>', $text); 248 + $text = preg_replace("#\s*\[/list:(o|u):{$this->uid}\]\n?\n?#", '</ol>', $text); 204 249 205 250 // list with "title", with it being just a list without style. 206 251 $text = preg_replace("#\[list=[^]]+:{$this->uid}\](.+?)(<li>|</ol>)#s", '<ul class="bbcode__list-title"><li>$1</li></ul><ol>$2', $text); ··· 236 281 { 237 282 $text = preg_replace("#\[quote=&quot;([^:]+)&quot;:{$this->uid}\]\s*#", '<blockquote><h4>\\1 wrote:</h4>', $text); 238 283 $text = preg_replace("#\[quote:{$this->uid}\]\s*#", '<blockquote>', $text); 239 - $text = preg_replace("#\s*\[/quote:{$this->uid}\]\s*#", '</blockquote>', $text); 284 + $text = preg_replace("#\s*\[/quote:{$this->uid}\]\n?\n?#", '</blockquote>', $text); 240 285 241 286 return $text; 242 287 } 243 288 244 289 // stolen from: www/forum/includes/functions.php:2845 245 - 246 290 public function parseSmilies($text) 247 291 { 248 292 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img class="smiley" src="'.osu_url('smilies').'/\2 />', $text); ··· 304 348 $text = $this->text; 305 349 306 350 // block 351 + $text = $this->parseImagemap($text); 307 352 $text = $this->parseBox($text); 308 353 $text = $this->parseCode($text); 309 354 $text = $this->parseList($text); 310 355 $text = $this->parseNotice($text); 311 356 $text = $this->parseQuote($text); 312 357 $text = $this->parseHeading($text); 313 - $text = $this->clearSpacesBetweenTags($text); 314 358 315 359 // inline 316 360 $text = $this->parseAudio($text);
+1
app/Libraries/CleanHTML.php
··· 28 28 $config->set('Cache.SerializerPath', $cachePath); 29 29 $config->set('Attr.AllowedRel', ['nofollow']); 30 30 $config->set('HTML.Trusted', true); 31 + $config->set('CSS.Trusted', true); 31 32 32 33 $def = $config->getHTMLDefinition(true); 33 34
+25
app/Libraries/LocaleMeta.php
··· 22 22 'flag' => 'BG', 23 23 'name' => 'Български', 24 24 ], 25 + 'ca' => [ 26 + 'flag' => 'AD', // ES-CA in crowdin 27 + 'name' => 'català', 28 + ], 25 29 'cs' => [ 26 30 'flag' => 'CZ', 27 31 'name' => 'Česky', ··· 51 55 'flag' => 'FI', 52 56 'name' => 'Suomi', 53 57 ], 58 + 'fil' => [ 59 + 'flag' => 'PH', 60 + 'name' => 'Wikang Filipino', 61 + ], 54 62 'fr' => [ 55 63 'flag' => 'FR', 56 64 'name' => 'français', 65 + ], 66 + 'he' => [ 67 + 'flag' => 'IL', 68 + 'name' => 'עִבְרִית‎', 57 69 ], 58 70 'hu' => [ 59 71 'flag' => 'HU', ··· 75 87 'flag' => 'KR', 76 88 'name' => '한국어', 77 89 ], 90 + 'lt' => [ 91 + 'flag' => 'LT', 92 + 'name' => 'lietuvių kalba', 93 + ], 78 94 'nl' => [ 79 95 'flag' => 'NL', 80 96 'name' => 'Nederlands', ··· 109 125 'sk' => [ 110 126 'flag' => 'SK', 111 127 'name' => 'Slovenčina', 128 + ], 129 + 'sl' => [ 130 + 'flag' => 'SI', 131 + 'name' => 'slovenščina', 132 + ], 133 + 'sr' => [ 134 + 'flag' => 'RS', 135 + 'moment' => 'sr-cyrl', 136 + 'name' => 'српски', 112 137 ], 113 138 'sv' => [ 114 139 'flag' => 'SE',
+2
app/Libraries/MorphMap.php
··· 10 10 use App\Models\Beatmapset; 11 11 use App\Models\Build; 12 12 use App\Models\Chat\Channel; 13 + use App\Models\Chat\Message; 13 14 use App\Models\Comment; 14 15 use App\Models\Forum; 15 16 use App\Models\LegacyMatch; ··· 30 31 Forum\Post::class => 'forum_post', 31 32 Forum\Topic::class => 'forum_topic', 32 33 LegacyMatch\Score::class => 'legacy_match_score', 34 + Message::class => 'message', 33 35 NewsPost::class => 'news_post', 34 36 Score\Best\Fruits::class => 'score_best_fruits', 35 37 Score\Best\Mania::class => 'score_best_mania',
+19 -1
app/Libraries/Search/BeatmapsetSearch.php
··· 14 14 use App\Models\Beatmapset; 15 15 use App\Models\Follow; 16 16 use App\Models\Score; 17 + use App\Models\User; 17 18 18 19 class BeatmapsetSearch extends RecordSearch 19 20 { ··· 85 86 $this->addRecommendedFilter($nested); 86 87 87 88 $this->addSimpleFilters($query, $nested); 89 + $this->addCreatorFilter($query, $nested); 88 90 $this->addTextFilter($query, 'artist', ['artist', 'artist_unicode']); 89 - $this->addTextFilter($query, 'creator', ['creator']); 90 91 91 92 $query->filter([ 92 93 'nested' => [ ··· 146 147 private function addBlockedUsersFilter($query) 147 148 { 148 149 $query->mustNot(['terms' => ['user_id' => $this->params->blockedUserIds()]]); 150 + } 151 + 152 + private function addCreatorFilter(BoolQuery $query, BoolQuery $nested): void 153 + { 154 + $value = $this->params->creator; 155 + 156 + if (!present($value)) { 157 + return; 158 + } 159 + 160 + $user = User::lookup($value); 161 + 162 + if ($user === null) { 163 + $this->addTextFilter($query, 'creator', ['creator']); 164 + } else { 165 + $nested->filter(['term' => ['beatmaps.user_id' => $user->getKey()]]); 166 + } 149 167 } 150 168 151 169 private function addExtraFilter($query)
+7
app/Libraries/Search/BeatmapsetSearchRequestParams.php
··· 9 9 use App\Libraries\Elasticsearch\Sort; 10 10 use App\Libraries\Elasticsearch\Utils\SearchAfterParam; 11 11 use App\Models\Beatmap; 12 + use App\Models\Beatmapset; 12 13 use App\Models\Genre; 13 14 use App\Models\Language; 14 15 use App\Models\User; ··· 308 309 309 310 // generic tie-breaker. 310 311 $this->sorts[] = new Sort('id', $sort->order); 312 + 313 + foreach ($this->sorts as $sort) { 314 + if ((Beatmapset::CASTS[$sort->field] ?? null) === 'datetime') { 315 + $sort->extras['missing'] = 0; 316 + } 317 + } 311 318 } 312 319 }
+7 -6
app/Libraries/Search/MultiSearch.php
··· 36 36 private $options; 37 37 private $query; 38 38 private $searches; 39 - private $request; 39 + private array $request; 40 40 41 41 public function __construct(Request $request, array $options = []) 42 42 { 43 - $this->query = trim($request['query']); 43 + $this->request = $request->all(); 44 + $this->query = trim(get_string($this->request['query'] ?? null) ?? ''); 44 45 $this->options = $options; 45 - $this->request = $request; 46 46 } 47 47 48 48 public function getMode() 49 49 { 50 - return presence($this->request['mode']) ?? 'all'; 50 + return presence($this->request['mode'] ?? null) ?? 'all'; 51 51 } 52 52 53 53 public function hasQuery() 54 54 { 55 - return present($this->query); 55 + return present($this->query) 56 + || ($this->getMode() === 'forum_post' && present(get_string($this->request['username'] ?? null))); 56 57 } 57 58 58 59 public function searches() ··· 70 71 $class = $settings['type']; 71 72 $paramsClass = $settings['paramsType']; 72 73 73 - $params = new $paramsClass($this->request->all(), $this->options['user']); 74 + $params = new $paramsClass($this->request, $this->options['user']); 74 75 $search = new $class($params); 75 76 if ($search instanceof BeatmapsetSearch) { 76 77 $search->source(false);
+30
app/Libraries/User/UserSignatures.php
··· 1 + <?php 2 + 3 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 + // See the LICENCE file in the repository root for full licence text. 5 + 6 + declare(strict_types=1); 7 + 8 + namespace App\Libraries\User; 9 + 10 + use App\Models\User; 11 + 12 + class UserSignatures 13 + { 14 + private array $html = []; 15 + 16 + public function get(User $user): ?string 17 + { 18 + $userId = $user->getKey(); 19 + 20 + if (!array_key_exists($userId, $this->html)) { 21 + $sig = $user->user_sig; 22 + 23 + $this->html[$userId] = present($sig) 24 + ? bbcode($user->user_sig, $user->user_sig_bbcode_uid) 25 + : null; 26 + } 27 + 28 + return $this->html[$userId]; 29 + } 30 + }
+2 -2
app/Models/BeatmapDiscussionPost.php
··· 226 226 $this->validationErrors()->add('message', 'required'); 227 227 } 228 228 229 - if (optional($this->beatmapDiscussion)->timestamp !== null && mb_strlen($this->message) > static::MESSAGE_LIMIT_TIMELINE) { 230 - $this->validationErrors()->add('message', 'too_long', ['limit' => static::MESSAGE_LIMIT_TIMELINE]); 229 + if ($this->beatmapDiscussion?->timestamp !== null) { 230 + $this->validateDbFieldLength(static::MESSAGE_LIMIT_TIMELINE, 'message'); 231 231 } 232 232 } 233 233
+9 -7
app/Models/Beatmapset.php
··· 108 108 { 109 109 use Memoizes, SoftDeletes, Traits\CommentableDefaults, Traits\Es\BeatmapsetSearch, Traits\Reportable, Validatable; 110 110 111 - protected $_storage = null; 112 - protected $table = 'osu_beatmapsets'; 113 - protected $primaryKey = 'beatmapset_id'; 114 - 115 - protected $casts = [ 111 + const CASTS = [ 116 112 'active' => 'boolean', 117 113 'approved_date' => 'datetime', 118 114 'comment_locked' => 'boolean', ··· 131 127 'video' => 'boolean', 132 128 ]; 133 129 134 - public $timestamps = false; 130 + const HYPEABLE_STATES = [-1, 0, 3]; 135 131 136 132 const STATES = [ 137 133 'graveyard' => -2, ··· 142 138 'qualified' => 3, 143 139 'loved' => 4, 144 140 ]; 145 - const HYPEABLE_STATES = [-1, 0, 3]; 141 + 142 + public $timestamps = false; 143 + 144 + protected $_storage = null; 145 + protected $casts = self::CASTS; 146 + protected $primaryKey = 'beatmapset_id'; 147 + protected $table = 'osu_beatmapsets'; 146 148 147 149 public static function coverSizes() 148 150 {
+1 -8
app/Models/Chat/Channel.php
··· 397 397 $this->validationErrors()->add('name', 'required'); 398 398 } 399 399 400 - foreach (static::MAX_FIELD_LENGTHS as $field => $limit) { 401 - if ($this->isDirty($field)) { 402 - $val = $this->$field; 403 - if ($val !== null && mb_strlen($val) > $limit) { 404 - $this->validationErrors()->add($field, 'too_long', ['limit' => $limit]); 405 - } 406 - } 407 - } 400 + $this->validateDbFieldLengths(); 408 401 409 402 return $this->validationErrors()->isEmpty(); 410 403 }
+19 -1
app/Models/Chat/Message.php
··· 5 5 6 6 namespace App\Models\Chat; 7 7 8 + use App\Models\Traits\Reportable; 9 + use App\Models\Traits\ReportableInterface; 8 10 use App\Models\User; 9 11 10 12 /** ··· 17 19 * @property \Carbon\Carbon $timestamp 18 20 * @property int $user_id 19 21 */ 20 - class Message extends Model 22 + class Message extends Model implements ReportableInterface 21 23 { 24 + use Reportable; 25 + 22 26 public ?string $uuid = null; 23 27 24 28 protected $primaryKey = 'message_id'; ··· 57 61 'timestamp_json' => $this->getJsonTimeFast($key), 58 62 59 63 'channel', 64 + 'reportedIn', 60 65 'sender' => $this->getRelationValue($key), 61 66 }; 67 + } 68 + 69 + public function trashed(): bool 70 + { 71 + return false; 72 + } 73 + 74 + protected function newReportableExtraParams(): array 75 + { 76 + return [ 77 + 'reason' => 'Spam', 78 + 'user_id' => $this->user_id, 79 + ]; 62 80 } 63 81 }
+8 -10
app/Models/Comment.php
··· 46 46 MorphMap::MAP[NewsPost::class], 47 47 ]; 48 48 49 - // FIXME: decide on good number. 50 - // some people seem to put song lyrics in comment which inflated the size. 51 - const MESSAGE_LIMIT = 10000; 49 + const MAX_FIELD_LENGTHS = [ 50 + // FIXME: decide on good number. 51 + // some people seem to put song lyrics in comment which inflated the size. 52 + 'message' => 10000, 53 + ]; 52 54 53 55 const SORTS = [ 54 56 'new' => [ ··· 119 121 120 122 public function setMessageAttribute($value) 121 123 { 122 - return $this->attributes['message'] = unzalgo($value); 124 + return $this->attributes['message'] = trim(unzalgo($value)); 123 125 } 124 126 125 127 public function votes() ··· 192 194 { 193 195 $this->validationErrors()->reset(); 194 196 195 - $messageLength = mb_strlen(trim($this->message)); 196 - 197 197 if ($this->isDirty('pinned') && $this->pinned && $this->parent_id !== null) { 198 198 $this->validationErrors()->add('pinned', '.top_only'); 199 199 } 200 200 201 - if ($messageLength === 0) { 201 + if (!present($this->message)) { 202 202 $this->validationErrors()->add('message', 'required'); 203 203 } 204 204 205 - if ($messageLength > static::MESSAGE_LIMIT) { 206 - $this->validationErrors()->add('message', 'too_long', ['limit' => static::MESSAGE_LIMIT]); 207 - } 205 + $this->validateDbFieldLengths(); 208 206 209 207 if ($this->isDirty('parent_id') && $this->parent_id !== null) { 210 208 if ($this->parent === null) {
+6
app/Models/Forum/Forum.php
··· 7 7 8 8 use App\Models\User; 9 9 use Carbon\Carbon; 10 + use Illuminate\Database\Eloquent\Builder; 10 11 11 12 /** 12 13 * @property bool $allow_topic_covers ··· 182 183 public function scopeDisplayList($query) 183 184 { 184 185 $query->orderBy('left_id'); 186 + } 187 + 188 + public function scopeSearchable(Builder $query): Builder 189 + { 190 + return $query->where('enable_indexing', true); 185 191 } 186 192 187 193 public function setForumParentsAttribute($value)
+1 -3
app/Models/Forum/Post.php
··· 355 355 } 356 356 } 357 357 358 - if ($this->isDirty('post_text') && mb_strlen($this->body_raw) > config('osu.forum.max_post_length')) { 359 - $this->validationErrors()->add('post_text', 'too_long', ['limit' => config('osu.forum.max_post_length')]); 360 - } 358 + $this->validateDbFieldLength(config('osu.forum.max_post_length'), 'post_text', 'body_raw'); 361 359 362 360 if (!$this->skipBeatmapPostRestrictions) { 363 361 // don't forget to sync with views.forum.topics._posts
+1 -9
app/Models/Forum/Topic.php
··· 333 333 $this->validationErrors()->add('topic_title', 'required'); 334 334 } 335 335 336 - foreach (static::MAX_FIELD_LENGTHS as $field => $limit) { 337 - if ($this->isDirty($field)) { 338 - $val = $this->$field; 339 - 340 - if (mb_strlen($val) > $limit) { 341 - $this->validationErrors()->add($field, 'too_long', ['limit' => $limit]); 342 - } 343 - } 344 - } 336 + $this->validateDbFieldLengths(); 345 337 346 338 return $this->validationErrors()->isEmpty(); 347 339 }
+6 -3
app/Models/Forum/TopicPoll.php
··· 18 18 private $params; 19 19 private $votedBy = []; 20 20 21 + public function __get(string $field) 22 + { 23 + return $this->params[$field]; 24 + } 25 + 21 26 public function canEdit() 22 27 { 23 28 return $this->topic->topic_time > Carbon::now()->subHours(config('osu.forum.poll_edit_hours')); ··· 60 65 $this->validationErrors()->add('title', 'required'); 61 66 } 62 67 63 - if (mb_strlen($this->params['title']) > 255) { 64 - $this->validationErrors()->add('title', 'too_long', ['limit' => 255]); 65 - } 68 + $this->validateFieldLength(255, 'title'); 66 69 67 70 if (count($this->params['options']) > count(array_unique($this->params['options']))) { 68 71 $this->validationErrors()->add('options', '.duplicate_options');
+1 -8
app/Models/User.php
··· 2275 2275 } 2276 2276 } 2277 2277 2278 - foreach (self::MAX_FIELD_LENGTHS as $field => $limit) { 2279 - if ($this->isDirty($field)) { 2280 - $val = $this->$field; 2281 - if ($val && mb_strlen($val) > $limit) { 2282 - $this->validationErrors()->add($field, '.too_long', ['limit' => $limit]); 2283 - } 2284 - } 2285 - } 2278 + $this->validateDbFieldLengths(); 2286 2279 2287 2280 if ($this->isDirty('group_id') && app('groups')->byId($this->group_id) === null) { 2288 2281 $this->validationErrors()->add('group_id', 'invalid');
+29 -19
app/Models/UserReport.php
··· 33 33 use RoutesNotifications, Validatable; 34 34 35 35 const BEATMAPSET_TYPE_REASONS = ['UnwantedContent', 'Other']; 36 - const MAX_LENGTH = 2000; 36 + const MAX_FIELD_LENGTHS = [ 37 + 'comments' => 2000, 38 + ]; 37 39 const POST_TYPE_REASONS = ['Insults', 'Spam', 'UnwantedContent', 'Nonsense', 'Other']; 38 40 const SCORE_TYPE_REASONS = ['Cheating', 'MultipleAccounts', 'Other']; 39 41 ··· 44 46 MorphMap::MAP[Best\Mania::class] => self::SCORE_TYPE_REASONS, 45 47 MorphMap::MAP[Best\Osu::class] => self::SCORE_TYPE_REASONS, 46 48 MorphMap::MAP[Best\Taiko::class] => self::SCORE_TYPE_REASONS, 49 + MorphMap::MAP[Chat\Message::class] => self::POST_TYPE_REASONS, 47 50 MorphMap::MAP[Comment::class] => self::POST_TYPE_REASONS, 48 51 MorphMap::MAP[Forum\Post::class] => self::POST_TYPE_REASONS, 49 52 MorphMap::MAP[Solo\Score::class] => self::SCORE_TYPE_REASONS, ··· 80 83 ) { 81 84 return config('osu.user_report_notification.endpoint_cheating'); 82 85 } else { 83 - return config('osu.user_report_notification.endpoint_moderation'); 86 + $type = match ($reportableModel::class) { 87 + BeatmapDiscussionPost::class => 'beatmapset_discussion', 88 + Beatmapset::class => 'beatmapset', 89 + Chat\Message::class => 'chat', 90 + Comment::class => 'comment', 91 + Forum\Post::class => 'forum', 92 + User::class => 'user', 93 + }; 94 + 95 + return config("osu.user_report_notification.endpoint.{$type}") 96 + ?? config('osu.user_report_notification.endpoint_moderation'); 84 97 } 85 98 } 86 99 ··· 93 106 { 94 107 $this->validationErrors()->reset(); 95 108 96 - if (!present(trim($this->comments))) { 109 + if (!present(trim($this->comments)) && (!($this->reportable instanceof Chat\Message) || $this->reason === 'Other')) { 97 110 $this->validationErrors()->add('comments', 'required'); 98 111 } 99 112 ··· 104 117 ); 105 118 } 106 119 107 - $allowedReasons = static::ALLOWED_REASONS[$this->reportable_type] ?? null; 108 - if ($allowedReasons !== null) { 109 - if (!in_array($this->reason, $allowedReasons, true)) { 110 - $this->validationErrors()->add( 111 - 'reason', 112 - '.reason_not_valid', 113 - ['reason' => $this->reason] 114 - ); 115 - } 116 - } 120 + $allowedReasons = static::ALLOWED_REASONS[$this->reportable_type] ?? [ 121 + ...static::BEATMAPSET_TYPE_REASONS, 122 + ...static::POST_TYPE_REASONS, 123 + ...static::SCORE_TYPE_REASONS, 124 + ]; 117 125 118 - if ($this->reportable instanceof Beatmapset && $this->reportable->isScoreable()) { 126 + if (!in_array($this->reason, $allowedReasons, true)) { 119 127 $this->validationErrors()->add( 120 128 'reason', 121 - '.no_ranked_beatmapset' 129 + '.reason_not_valid', 130 + ['reason' => $this->reason] 122 131 ); 123 132 } 124 133 125 - if (mb_strlen($this->comments) > static::MAX_LENGTH) { 134 + if ($this->reportable instanceof Beatmapset && $this->reportable->isScoreable()) { 126 135 $this->validationErrors()->add( 127 - 'comments', 128 - 'too_long', 129 - ['limit' => static::MAX_LENGTH] 136 + 'reason', 137 + '.no_ranked_beatmapset' 130 138 ); 131 139 } 140 + 141 + $this->validateDbFieldLengths(); 132 142 133 143 return $this->validationErrors()->isEmpty(); 134 144 }
+11 -2
app/Notifications/UserReportNotification.php
··· 52 52 ->color('warning') 53 53 ->content($notifiable->comments) 54 54 ->fields([ 55 - 'Reporter' => "<{$this->reporter->url()}|{$this->reporter->username}>", 55 + 'Reporter' => $this->discordMarkdownLink($this->reporter->url(), $this->reporter->username), 56 56 'Reported' => $reportedText, 57 - 'User' => "<{$userUrl}|{$user}>", 57 + 'User' => $this->discordMarkdownLink($userUrl, $user), 58 58 'Reason' => $notifiable->reason, 59 59 ]); 60 60 }); ··· 63 63 public function via($notifiable) 64 64 { 65 65 return ['slack']; 66 + } 67 + 68 + private function discordMarkdownLink(string $url, string $text): string 69 + { 70 + // I couldn't find any way to escape them so this seems to be the next best thing. 71 + // The alternative characters were taken from https://github.com/python-discord/sir-lancebot/pull/820 72 + $text = strtr($text, ['[' => '⦋', ']' => '⦌']); 73 + 74 + return "[{$text}]({$url})"; 66 75 } 67 76 }
+23
app/Traits/Validatable.php
··· 29 29 30 30 return $this->_validationErrors; 31 31 } 32 + 33 + private function validateDbFieldLength(int $limit, string $dbField, ?string $checkField = null): void 34 + { 35 + if ($this->isDirty($dbField)) { 36 + $this->validateFieldLength($limit, $dbField, $checkField); 37 + } 38 + } 39 + 40 + private function validateDbFieldLengths(): void 41 + { 42 + foreach (static::MAX_FIELD_LENGTHS as $field => $limit) { 43 + $this->validateDbFieldLength($limit, $field, $field); 44 + } 45 + } 46 + 47 + private function validateFieldLength(int $limit, string $field, ?string $checkField = null): void 48 + { 49 + $checkField ??= $field; 50 + $val = $this->$checkField; 51 + if ($val !== null && mb_strlen($val) > $limit) { 52 + $this->validationErrors()->add($field, 'too_long', ['limit' => $limit]); 53 + } 54 + } 32 55 }
+17 -8
app/helpers.php
··· 1216 1216 return $formatter->format($datetime); 1217 1217 } 1218 1218 1219 + function i18n_date_auto(DateTimeInterface $date, string $skeleton): string 1220 + { 1221 + $locale = App::getLocale(); 1222 + $generator = new IntlDatePatternGenerator($locale); 1223 + $pattern = $generator->getBestPattern($skeleton); 1224 + 1225 + return IntlDateFormatter::formatObject($date, $pattern, $locale); 1226 + } 1227 + 1219 1228 function i18n_number_format($number, $style = null, $pattern = null, $precision = null, $locale = null) 1220 1229 { 1221 1230 if ($style === null && $pattern === null && $precision === null) { ··· 1714 1723 } 1715 1724 1716 1725 // e.g. 100634983048665 -> 100.63 trillion 1717 - function suffixed_number_format($number) 1726 + function suffixed_number_format(float|int $number, ?string $locale = null): string 1718 1727 { 1719 - $suffixes = ['', 'k', 'million', 'billion', 'trillion']; // TODO: localize 1720 - $k = 1000; 1728 + $locale ??= App::getLocale(); 1721 1729 1722 - if ($number < $k) { 1723 - return $number; 1724 - } 1730 + static $formatters = []; 1725 1731 1726 - $i = floor(log($number) / log($k)); 1732 + if (!isset($formatters[$locale])) { 1733 + $formatters[$locale] = new NumberFormatter($locale, NumberFormatter::PADDING_POSITION); 1734 + $formatters[$locale]->setAttribute(NumberFormatter::FRACTION_DIGITS, 2); 1735 + } 1727 1736 1728 - return number_format($number / pow($k, $i), 2).' '.$suffixes[$i]; 1737 + return $formatters[$locale]->format($number); 1729 1738 } 1730 1739 1731 1740 function suffixed_number_format_tag($number)
+13 -1
config/app.php
··· 81 81 82 82 /* 83 83 * Make sure to check locale name mapping for other components. 84 - * Carbon is in Http\Middleware\SetLocale (no helper... yet?). 84 + * carbon is in Http\Middleware\SetLocale (no helper... yet?). 85 85 * html, momentjs, and laravel are in LocaleMeta. 86 + * php (IntlDateFormatter etc) isn't mapped at the moment. 86 87 * Check respective packages for supported list of languages. 88 + * 89 + * carbon: list in vendor/nesbot/carbon/src/Carbon/Lang/ 90 + * html: lang attribute in html tag. Mainly for uppercasing country code if used. 91 + * laravel: list in vendor/laravel/framework/src/Illuminate/Translation/MessageSelector.php 92 + * momentjs: list in node_modules/moment/locale/ 87 93 */ 88 94 'available_locales' => [ 89 95 // separate the default ··· 93 99 'ar', 94 100 'be', 95 101 'bg', 102 + 'ca', 96 103 'cs', 97 104 'da', 98 105 'de', 99 106 'el', 100 107 'es', 101 108 'fi', 109 + 'fil', 102 110 'fr', 111 + 'he', 103 112 'hu', 104 113 'id', 105 114 'it', 106 115 'ja', 107 116 'ko', 117 + 'lt', 108 118 'nl', 109 119 'no', 110 120 'pl', ··· 113 123 'ro', 114 124 'ru', 115 125 'sk', 126 + 'sl', 127 + 'sr', 116 128 'sv', 117 129 'th', 118 130 'tr',
+10 -1
config/osu.php
··· 269 269 'ban_persist_days' => get_int(env('BAN_PERSIST_DAYS')) ?? 28, 270 270 ], 271 271 'user_report_notification' => [ 272 + 'endpoint_cheating' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_CHEATING')), 272 273 'endpoint_moderation' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_MODERATION')), 273 - 'endpoint_cheating' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_CHEATING')), 274 + 275 + 'endpoint' => [ 276 + 'beatmapset_discussion' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_BEATMAPSET_DISCUSSION')), 277 + 'beatmapset' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_BEATMAPSET')), 278 + 'chat' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_CHAT')), 279 + 'comment' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_COMMENT')), 280 + 'forum' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_FORUM')), 281 + 'user' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_USER')), 282 + ], 274 283 ], 275 284 'wiki' => [ 276 285 'branch' => presence(env('WIKI_BRANCH'), 'master'),
+6
crowdin.yml
··· 8 8 ar: ar 9 9 be: be 10 10 bg: bg 11 + ca: ca 11 12 cs: cs 12 13 da: da 13 14 de: de ··· 15 16 es-ES: es 16 17 fi-FI: fi 17 18 fi: fi 19 + fil: fil 18 20 fr: fr 21 + he: he 19 22 hu: hu 20 23 id: id 21 24 it: it 22 25 ja: ja 23 26 ko: ko 27 + lt: lt 24 28 nl: nl 25 29 'no': 'no' 26 30 pl: pl ··· 29 33 ro: ro 30 34 ru: ru 31 35 sk: sk 36 + sl: sl 37 + sr: sr 32 38 sv-SE: sv 33 39 th: th 34 40 tr: tr
+22 -16
database/factories/ArtistFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - $factory->define(App\Models\Artist::class, function (Faker\Generator $faker) { 7 - return [ 8 - 'name' => function () use ($faker) { 9 - return $faker->lastName().' '.$faker->colorName(); 10 - }, 11 - 'description' => function () use ($faker) { 12 - return $faker->realText(); 13 - }, 14 - 'website' => function () use ($faker) { 15 - return $faker->safeEmailDomain(); 16 - }, 17 - 'cover_url' => '/images/headers/generic.jpg', 18 - 'header_url' => '/images/headers/generic.jpg', 19 - 'visible' => 1, 20 - ]; 21 - }); 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 9 + 10 + use App\Models\Artist; 11 + 12 + class ArtistFactory extends Factory 13 + { 14 + protected $model = Artist::class; 15 + 16 + public function definition(): array 17 + { 18 + return [ 19 + 'name' => fn() => "{$this->faker->lastName()} {$this->faker->colorName()}", 20 + 'description' => fn() => $this->faker->realText(), 21 + 'website' => fn() => $this->faker->safeEmailDomain(), 22 + 'cover_url' => '/images/headers/generic.jpg', 23 + 'header_url' => '/images/headers/generic.jpg', 24 + 'visible' => 1, 25 + ]; 26 + } 27 + }
+18 -18
database/factories/BanchoStatsFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - /* 7 - |-------------------------------------------------------------------------- 8 - | Model Factories 9 - |-------------------------------------------------------------------------- 10 - | 11 - | Here you may define all of your model factories. Model factories give 12 - | you a convenient way to create models for testing and seeding your 13 - | database. Just tell the factory how a default model should look. 14 - | 15 - */ 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 16 9 10 + use App\Models\BanchoStats; 17 11 use Carbon\Carbon; 18 12 19 - $factory->define(App\Models\BanchoStats::class, function (Faker\Generator $faker) { 20 - return [ 21 - 'users_irc' => 100 + $faker->randomNumber(2), 22 - 'users_osu' => 10000 + $faker->randomNumber(4), 23 - 'multiplayer_games' => 200 + $faker->randomNumber(3), 24 - 'date' => new Carbon(), 25 - ]; 26 - }); 13 + class BanchoStatsFactory extends Factory 14 + { 15 + protected $model = BanchoStats::class; 16 + 17 + public function definition(): array 18 + { 19 + return [ 20 + 'users_irc' => fn() => 100 + $this->faker->randomNumber(2), 21 + 'users_osu' => fn() => 10000 + $this->faker->randomNumber(4), 22 + 'multiplayer_games' => fn() => 200 + $this->faker->randomNumber(3), 23 + 'date' => fn() => Carbon::now(), 24 + ]; 25 + } 26 + }
+29 -10
database/factories/BeatmapFailtimesFactory.php
··· 2 2 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 - $factory->define(App\Models\BeatmapFailtimes::class, function (Faker\Generator $faker) { 6 - $array = []; 5 + 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 9 + 10 + use App\Models\BeatmapFailtimes; 11 + 12 + class BeatmapFailtimesFactory extends Factory 13 + { 14 + protected $model = BeatmapFailtimes::class; 15 + 16 + public function definition(): array 17 + { 18 + $array = []; 19 + 20 + for ($i = 1; $i <= 100; $i++) { 21 + $field = 'p'.strval($i); 22 + $array[$field] = rand(1, 10000); 23 + } 7 24 8 - for ($i = 1; $i <= 100; $i++) { 9 - $field = 'p'.strval($i); 10 - $array = array_merge($array, [$field => rand(1, 10000)]); 25 + return $array; 11 26 } 12 27 13 - return $array; 14 - }); 28 + public function fail(): static 29 + { 30 + return $this->state(['type' => 'fail']); 31 + } 15 32 16 - $factory->state(App\Models\BeatmapFailtimes::class, 'fail', ['type' => 'fail']); 17 - 18 - $factory->state(App\Models\BeatmapFailtimes::class, 'retry', ['type' => 'exit']); 33 + public function retry(): static 34 + { 35 + return $this->state(['type' => 'exit']); 36 + } 37 + }
+26 -17
database/factories/BeatmapMirrorFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 9 + 6 10 use App\Models\BeatmapMirror; 7 11 8 - $factory->define(BeatmapMirror::class, function (Faker\Generator $faker) { 9 - return [ 10 - 'base_url' => 'http://beatmap-download.test/', 11 - 'traffic_used' => rand(0, pow(2, 32)), 12 - 'secret_key' => function () use ($faker) { 13 - return $faker->password(); 14 - }, 15 - 'provider_user_id' => 2, 16 - 'enabled' => 1, 17 - 'version' => BeatmapMirror::MIN_VERSION_TO_USE, 18 - ]; 19 - }); 12 + class BeatmapMirrorFactory extends Factory 13 + { 14 + protected $model = BeatmapMirror::class; 15 + 16 + public function default(): static 17 + { 18 + return $this->state([ 19 + 'mirror_id' => config('osu.beatmap_processor.mirrors_to_use')[0], 20 + ]); 21 + } 20 22 21 - $factory->state(BeatmapMirror::class, 'default', function () { 22 - return [ 23 - 'mirror_id' => config('osu.beatmap_processor.mirrors_to_use')[0], 24 - ]; 25 - }); 23 + public function definition(): array 24 + { 25 + return [ 26 + 'base_url' => 'http://beatmap-download.test/', 27 + 'traffic_used' => rand(0, pow(2, 32)), 28 + 'secret_key' => fn() => $this->faker->password(), 29 + 'provider_user_id' => 2, 30 + 'enabled' => 1, 31 + 'version' => BeatmapMirror::MIN_VERSION_TO_USE, 32 + ]; 33 + } 34 + }
+19 -23
database/factories/ChangelogFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - /* 7 - |-------------------------------------------------------------------------- 8 - | Model Factories 9 - |-------------------------------------------------------------------------- 10 - | 11 - | Here you may define all of your model factories. Model factories give 12 - | you a convenient way to create models for testing and seeding your 13 - | database. Just tell the factory how a default model should look. 14 - | 15 - */ 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 16 9 10 + use App\Models\Changelog; 17 11 use App\Models\User; 18 12 19 - $factory->define(App\Models\Changelog::class, function (Faker\Generator $faker) { 20 - return [ 21 - 'user_id' => function () { 22 - $u = User::inRandomOrder()->first() ?? User::factory()->create(); 13 + class ChangelogFactory extends Factory 14 + { 15 + protected $model = Changelog::class; 23 16 24 - return $u->getKey(); 25 - }, 26 - 'prefix' => $faker->randomElement(['*', '+', '?']), 27 - 'category' => $faker->randomElement(['Web', 'Audio', 'Code', 'Editor', 'Gameplay', 'Graphics']), 28 - 'message' => $faker->catchPhrase, 29 - 'checksum' => $faker->md5, 30 - 'date' => $faker->dateTimeBetween('-6 weeks', 'now'), 31 - ]; 32 - }); 17 + public function definition(): array 18 + { 19 + return [ 20 + 'user_id' => User::factory(), 21 + 'prefix' => fn() => $this->faker->randomElement(['*', '+', '?']), 22 + 'category' => fn() => $this->faker->randomElement(['Web', 'Audio', 'Code', 'Editor', 'Gameplay', 'Graphics']), 23 + 'message' => fn() => $this->faker->catchPhrase(), 24 + 'checksum' => fn() => $this->faker->md5, 25 + 'date' => fn() => $this->faker->dateTimeBetween('-6 weeks'), 26 + ]; 27 + } 28 + }
+18 -6
database/factories/ChatFilterFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - $factory->define(App\Models\ChatFilter::class, function (Faker\Generator $faker) { 7 - return [ 8 - 'match' => $faker->unique()->word, 9 - 'replacement' => $faker->word, 10 - ]; 11 - }); 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 9 + 10 + use App\Models\ChatFilter; 11 + 12 + class ChatFilterFactory extends Factory 13 + { 14 + protected $model = ChatFilter::class; 15 + 16 + public function definition(): array 17 + { 18 + return [ 19 + 'match' => fn() => $this->faker->unique()->word, 20 + 'replacement' => fn() => $this->faker->word, 21 + ]; 22 + } 23 + }
+21 -10
database/factories/ContestEntryFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - $factory->define(App\Models\ContestEntry::class, function (Faker\Generator $faker) { 7 - return [ 8 - 'user_id' => function () { 9 - return App\Models\User::factory()->create()->user_id; 10 - }, 11 - 'entry_url' => '/images/headers/generic.jpg', 12 - 'name' => $faker->words(3, true), 13 - 'masked_name' => $faker->words(3, true), 14 - ]; 15 - }); 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 9 + 10 + use App\Models\ContestEntry; 11 + use App\Models\User; 12 + 13 + class ContestEntryFactory extends Factory 14 + { 15 + protected $model = ContestEntry::class; 16 + 17 + public function definition(): array 18 + { 19 + return [ 20 + 'user_id' => User::factory(), 21 + 'entry_url' => '/images/headers/generic.jpg', 22 + 'name' => fn() => $this->faker->words(3, true), 23 + 'masked_name' => fn() => $this->faker->words(3, true), 24 + ]; 25 + } 26 + }
+59 -48
database/factories/ContestFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - $factory->define(App\Models\Contest::class, function (Faker\Generator $faker) { 7 - return [ 8 - 'name' => function () use ($faker) { 9 - return $faker->sentence(); 10 - }, 11 - 'description_enter' => function () use ($faker) { 12 - return $faker->paragraph(); 13 - }, 14 - 'description_voting' => function () use ($faker) { 15 - return $faker->paragraph(); 16 - }, 17 - 'type' => 'art', 18 - 'header_url' => '/images/headers/generic.jpg', 19 - 'visible' => 1, 20 - ]; 21 - }); 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 22 9 23 - $factory->state(App\Models\Contest::class, 'pending', function (Faker\Generator $faker) { 24 - return [ 25 - 'entry_starts_at' => Carbon\Carbon::now()->addMonths(1), 26 - 'entry_ends_at' => Carbon\Carbon::now()->addMonths(2), 27 - 'voting_starts_at' => Carbon\Carbon::now()->addMonths(3), 28 - 'voting_ends_at' => Carbon\Carbon::now()->addMonths(4), 29 - ]; 30 - }); 10 + use App\Models\Contest; 11 + use Carbon\Carbon; 12 + 13 + class ContestFactory extends Factory 14 + { 15 + protected $model = Contest::class; 16 + 17 + public function completed(): static 18 + { 19 + return $this->state([ 20 + 'entry_starts_at' => fn() => Carbon::now()->subMonths(4), 21 + 'entry_ends_at' => fn() => Carbon::now()->subMonths(3), 22 + 'voting_starts_at' => fn() => Carbon::now()->subMonths(2), 23 + 'voting_ends_at' => fn() => Carbon::now()->subMonths(1), 24 + ]); 25 + } 26 + 27 + public function definition(): array 28 + { 29 + return [ 30 + 'name' => fn() => $this->faker->sentence(), 31 + 'description_enter' => fn() => $this->faker->paragraph(), 32 + 'description_voting' => fn() => $this->faker->paragraph(), 33 + 'type' => 'art', 34 + 'header_url' => '/images/headers/generic.jpg', 35 + 'visible' => 1, 36 + ]; 37 + } 31 38 32 - $factory->state(App\Models\Contest::class, 'entry', function (Faker\Generator $faker) { 33 - return [ 34 - 'entry_starts_at' => Carbon\Carbon::now()->subMonths(1), 35 - 'entry_ends_at' => Carbon\Carbon::now()->addMonths(1), 36 - 'voting_starts_at' => Carbon\Carbon::now()->addMonths(2), 37 - 'voting_ends_at' => Carbon\Carbon::now()->addMonths(3), 38 - ]; 39 - }); 39 + public function entry(): static 40 + { 41 + return $this->state([ 42 + 'entry_starts_at' => fn() => Carbon::now()->subMonths(1), 43 + 'entry_ends_at' => fn() => Carbon::now()->addMonths(1), 44 + 'voting_starts_at' => fn() => Carbon::now()->addMonths(2), 45 + 'voting_ends_at' => fn() => Carbon::now()->addMonths(3), 46 + ]); 47 + } 40 48 41 - $factory->state(App\Models\Contest::class, 'voting', function (Faker\Generator $faker) { 42 - return [ 43 - 'entry_starts_at' => Carbon\Carbon::now()->subMonths(3), 44 - 'entry_ends_at' => Carbon\Carbon::now()->subMonths(2), 45 - 'voting_starts_at' => Carbon\Carbon::now()->subMonths(1), 46 - 'voting_ends_at' => Carbon\Carbon::now()->addMonths(1), 47 - ]; 48 - }); 49 + public function pending(): static 50 + { 51 + return $this->state([ 52 + 'entry_starts_at' => fn() => Carbon::now()->addMonths(1), 53 + 'entry_ends_at' => fn() => Carbon::now()->addMonths(2), 54 + 'voting_starts_at' => fn() => Carbon::now()->addMonths(3), 55 + 'voting_ends_at' => fn() => Carbon::now()->addMonths(4), 56 + ]); 57 + } 49 58 50 - $factory->state(App\Models\Contest::class, 'completed', function (Faker\Generator $faker) { 51 - return [ 52 - 'entry_starts_at' => Carbon\Carbon::now()->subMonths(4), 53 - 'entry_ends_at' => Carbon\Carbon::now()->subMonths(3), 54 - 'voting_starts_at' => Carbon\Carbon::now()->subMonths(2), 55 - 'voting_ends_at' => Carbon\Carbon::now()->subMonths(1), 56 - ]; 57 - }); 59 + public function voting(): static 60 + { 61 + return $this->state([ 62 + 'entry_starts_at' => fn() => Carbon::now()->subMonths(3), 63 + 'entry_ends_at' => fn() => Carbon::now()->subMonths(2), 64 + 'voting_starts_at' => fn() => Carbon::now()->subMonths(1), 65 + 'voting_ends_at' => fn() => Carbon::now()->addMonths(1), 66 + ]); 67 + } 68 + }
+18 -10
database/factories/GroupFactory.php
··· 3 3 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 - $factory->define(App\Models\Group::class, function (Faker\Generator $faker) { 7 - return [ 8 - 'group_name' => function () use ($faker) { 9 - return $faker->colorName().' '.$faker->domainWord(); 10 - }, 11 - 'group_desc' => function () use ($faker) { 12 - return $faker->sentence(); 13 - }, 14 - ]; 15 - }); 6 + declare(strict_types=1); 7 + 8 + namespace Database\Factories; 9 + 10 + use App\Models\Group; 11 + 12 + class GroupFactory extends Factory 13 + { 14 + protected $model = Group::class; 15 + 16 + public function definition(): array 17 + { 18 + return [ 19 + 'group_name' => fn() => "{$this->faker->colorName()} {$this->faker->domainWord()}", 20 + 'group_desc' => fn() => $this->faker->sentence(), 21 + ]; 22 + } 23 + }
+33
database/migrations/2023_03_22_000000_add_chat_message_to_report_types.php
··· 1 + <?php 2 + 3 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4 + // See the LICENCE file in the repository root for full licence text. 5 + 6 + use Illuminate\Database\Migrations\Migration; 7 + 8 + return new class extends Migration 9 + { 10 + /** 11 + * Run the migrations. 12 + * 13 + * @return void 14 + */ 15 + public function up() 16 + { 17 + DB::statement("ALTER TABLE osu_user_reports 18 + MODIFY COLUMN reportable_type 19 + enum('user', 'comment', 'score_best_osu', 'score_best_taiko', 'score_best_fruits', 'score_best_mania', 'beatmapset_discussion_post', 'forum_post', 'beatmapset', 'solo_score', 'message')"); 20 + } 21 + 22 + /** 23 + * Reverse the migrations. 24 + * 25 + * @return void 26 + */ 27 + public function down() 28 + { 29 + DB::statement("ALTER TABLE osu_user_reports 30 + MODIFY COLUMN reportable_type 31 + enum('user', 'comment', 'score_best_osu', 'score_best_taiko', 'score_best_fruits', 'score_best_mania', 'beatmapset_discussion_post', 'forum_post', 'beatmapset', 'solo_score')"); 32 + } 33 + };
+19
database/mods.json
··· 1330 1330 "ValidForMultiplayerAsFreeMod": true 1331 1331 }, 1332 1332 { 1333 + "Acronym": "SG", 1334 + "Name": "Single Tap", 1335 + "Description": "One key for dons, one key for kats.", 1336 + "Type": "Conversion", 1337 + "Settings": [], 1338 + "IncompatibleMods": [ 1339 + "AT", 1340 + "CN", 1341 + "RX" 1342 + ], 1343 + "RequiresConfiguration": false, 1344 + "UserPlayable": true, 1345 + "ValidForMultiplayer": true, 1346 + "ValidForMultiplayerAsFreeMod": true 1347 + }, 1348 + { 1333 1349 "Acronym": "AT", 1334 1350 "Name": "Autoplay", 1335 1351 "Description": "Watch a perfect automated play through the song.", ··· 1340 1356 "SD", 1341 1357 "PF", 1342 1358 "AC", 1359 + "SG", 1343 1360 "CN", 1344 1361 "RX", 1345 1362 "AS" ··· 1360 1377 "SD", 1361 1378 "PF", 1362 1379 "AC", 1380 + "SG", 1363 1381 "AT", 1364 1382 "CN", 1365 1383 "RX", ··· 1381 1399 "SD", 1382 1400 "PF", 1383 1401 "AC", 1402 + "SG", 1384 1403 "AT", 1385 1404 "CN" 1386 1405 ],
+8 -11
database/seeders/ModelSeeders/BanchoStatsSeeder.php
··· 13 13 { 14 14 /** 15 15 * Run the database seeds. 16 - * 17 - * @return void 18 16 */ 19 - public function run() 17 + public function run(): void 20 18 { 21 - $date = new Carbon(); 19 + $timestamp = Carbon::now(); 22 20 23 - //Create 500 new data points 24 - factory(BanchoStats::class, 500)->make()->each(function ($stat) use ($date) { 25 - $stat->date = $date; 26 - $stat->save(); 21 + // Create 500 new data points 22 + for ($i = 0; $i < 500; $i++) { 23 + BanchoStats::factory()->create(['date' => $timestamp]); 27 24 28 - //Increment the dates by 5 each time 29 - $date->addMinutes(5); 30 - }); 25 + // Increment the timestamp by 5 each time 26 + $timestamp->addMinutes(5); 27 + } 31 28 } 32 29 }
+2 -2
database/seeders/ModelSeeders/BeatmapSeeder.php
··· 171 171 BeatmapFailtimes::where('beatmap_id', $beatmap->beatmap_id)->delete(); 172 172 173 173 $beatmap->failtimes()->saveMany([ 174 - factory(BeatmapFailtimes::class)->states('fail')->make(), 175 - factory(BeatmapFailtimes::class)->states('retry')->make(), 174 + BeatmapFailtimes::factory()->fail()->make(), 175 + BeatmapFailtimes::factory()->retry()->make(), 176 176 ]); 177 177 } 178 178
+3 -3
database/seeders/ModelSeeders/ChangelogSeeder.php
··· 37 37 ->merge(Build::factory()->count(5)->create(['stream_id' => $fallback->stream_id])); 38 38 39 39 foreach ($builds as $build) { 40 - factory(Changelog::class, 5)->create([ 40 + Changelog::factory()->count(5)->create([ 41 41 'build' => $build->version, 42 42 'stream_id' => $build->stream_id, 43 43 ]); 44 44 } 45 45 46 46 // create some buildless changes 47 - factory(Changelog::class, 15)->create([ 47 + Changelog::factory()->count(15)->create([ 48 48 'build' => null, 49 49 'stream_id' => 5, 50 50 ]); 51 51 52 - factory(Changelog::class, 5)->create([ 52 + Changelog::factory()->count(5)->create([ 53 53 'build' => null, 54 54 'stream_id' => 1, 55 55 ]);
+18 -1
docker-compose.yml
··· 2 2 3 3 x-env: &x-env 4 4 APP_KEY: "${APP_KEY}" 5 - BEATMAPS_DIFFICULTY_CACHE_SERVER_URL: http://beatmap-difficulty-lookup-cache:5000 5 + BEATMAPS_DIFFICULTY_CACHE_SERVER_URL: http://beatmap-difficulty-lookup-cache 6 6 BROADCAST_DRIVER: redis 7 7 CACHE_DRIVER: redis 8 8 DB_CONNECTION_STRING: Server=db;Database=osu;Uid=osuweb; ··· 159 159 environment: 160 160 <<: *x-env 161 161 SCHEMA: "${SCHEMA:-1}" 162 + 163 + score-indexer-test: 164 + image: pppy/osu-elastic-indexer 165 + command: ["queue", "watch", "--force-version"] 166 + depends_on: 167 + redis: 168 + condition: service_healthy 169 + db: 170 + condition: service_healthy 171 + elasticsearch: 172 + condition: service_healthy 173 + environment: 174 + <<: *x-env 175 + # match with .env.testing.example 176 + DB_CONNECTION_STRING: Server=db;Database=osu_test;Uid=osuweb; 177 + ES_INDEX_PREFIX: test_ 178 + SCHEMA: test 162 179 163 180 volumes: 164 181 database:
+8 -1
docker/development/entrypoint.sh
··· 62 62 fi 63 63 64 64 case "$command" in 65 - browser) _rexec php /app/artisan dusk --verbose "$@";; 65 + browser) _test_browser "$@";; 66 66 js) _rexec yarn karma start --single-run --browsers ChromeHeadless "$@";; 67 67 phpunit) _rexec ./bin/phpunit.sh "$@";; 68 68 esac 69 69 } 70 + 71 + _test_browser() { 72 + export APP_ENV=dusk.local 73 + export OCTANE_STATE_FILE=/app/storage/logs/octane-server-state-dusk.json 74 + _rexec ./bin/run_dusk.sh "$@" 75 + } 76 + 70 77 71 78 _watch() { 72 79 _run yarn --network-timeout 100000
+1
resources/css/bem-index.less
··· 188 188 @import "bem/header-v4"; 189 189 @import "bem/icon-dropdown-menu"; 190 190 @import "bem/icon-stack"; 191 + @import "bem/imagemap"; 191 192 @import "bem/input-container"; 192 193 @import "bem/js-accordion"; 193 194 @import "bem/js-flash-border";
+2
resources/css/bem/bbcode-spoilerbox.less
··· 21 21 .link-default(); 22 22 text-align: left; 23 23 display: flex; 24 + flex-wrap: wrap; 25 + overflow-wrap: anywhere; 24 26 font-weight: bold; 25 27 26 28 &:hover {
+12 -23
resources/css/bem/beatmapset-header.less
··· 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 4 .beatmapset-header { 5 - @header-height: 350px; 5 + display: grid; 6 + gap: 20px; 7 + padding: 10px @gutter-beatmapset 0; 8 + min-height: 350px; 9 + position: relative; 6 10 7 - .default-box-shadow(); 11 + @media @desktop { 12 + padding: 20px @gutter-beatmapset-desktop 0; 13 + grid-template-columns: 1fr @beatmapset-float-box-width; 14 + } 8 15 9 16 &__box { 10 17 display: flex; 11 18 flex-direction: column; 12 19 13 - padding: 20px 10px 25px; 14 20 position: relative; 15 21 16 22 &--main { 17 - align-self: stretch; 18 - min-width: 0; 19 - 20 23 @media @desktop { 21 - flex: 1; 24 + padding-bottom: 25px; 22 25 } 23 26 } 24 27 25 28 &--stats { 26 - padding-bottom: 0; 27 - align-self: stretch; 28 29 justify-content: space-between; 29 30 } 30 31 } ··· 39 40 margin-top: 10px; 40 41 } 41 42 42 - &__content { 43 - display: flex; 44 - flex-direction: column; 45 - padding: 0 30px; 46 - min-height: @header-height; 47 - position: relative; 48 - 49 - @media @desktop { 50 - align-items: flex-end; 51 - flex-direction: row; 52 - } 53 - 54 - } 55 - 56 43 &__availability-info { 57 44 font-size: @font-size--title-small; 58 45 padding: 10px; ··· 69 56 &__cover { 70 57 --border-radius: 0; 71 58 .light-header-overlay(); 59 + display: contents; 72 60 } 73 61 74 62 &__details-text { ··· 110 98 &__diff-name { 111 99 font-size: 17px; 112 100 font-weight: 600; 101 + overflow-wrap: anywhere; 113 102 114 103 color: hsl(var(--hsl-c1)); 115 104
+42 -57
resources/css/bem/beatmapset-info.less
··· 3 3 4 4 .beatmapset-info { 5 5 @_top: beatmapset-info; 6 - @min-height: 220px; 7 - @max-height: 320px; 6 + @min-height: 200px; 7 + @max-height: 300px; 8 8 @box-margin--top: 15px; 9 - @header-margin--top: 15px; 10 - @header-margin--bottom: 5px; 11 9 12 10 font-size: @font-size--normal; 13 - color: white; 14 11 15 - display: flex; 16 - flex-direction: column; 12 + display: grid; 13 + gap: 20px; 17 14 18 - padding: 0 30px 0; 15 + padding: 10px @gutter-beatmapset 0; 19 16 background-color: hsl(var(--hsl-b4)); 17 + -webkit-overflow-scrolling: touch; 20 18 21 19 @media @desktop { 22 - flex-direction: row; 23 - min-height: @min-height; 24 - max-height: @max-height; 20 + padding: 15px @gutter-beatmapset-desktop 0; 21 + grid-template-columns: 1fr 175px @beatmapset-float-box-width; 25 22 } 26 23 27 24 &__box { 28 - margin: @box-margin--top 10px 0; 29 - flex: none; 30 - 31 - &--description { 32 - flex: 1; 33 - min-width: 0; 34 - display: flex; 35 - flex-direction: column; 36 - position: relative; 37 - } 25 + min-width: 0; 26 + display: flex; 27 + flex-direction: column; 28 + position: relative; 29 + padding-top: 10px; 38 30 39 - &--meta { 40 - position: relative; 41 - 42 - @media @desktop { 43 - width: 175px; 44 - overflow: hidden; 45 - margin-bottom: 10px; 46 - } 31 + @media @desktop { 32 + min-height: @min-height; 33 + max-height: @max-height; 47 34 } 48 35 49 36 &--success-rate { 50 - @media @desktop { 51 - width: @beatmapset-float-box-width; 52 - } 37 + padding-top: 0; 53 38 } 54 39 } 55 40 56 - &__description { 57 - -webkit-overflow-scrolling: touch; 58 - overflow-y: auto; 59 - padding-right: 10px; 60 - 61 - // to prevent overflow from extending container box 62 - @media @desktop { 63 - .full-size(); 64 - } 65 - @media @mobile { 66 - max-height: @max-height; 67 - } 68 - } 69 - 70 - &__description-container { 71 - min-height: 0; 72 - max-height: @max-height; 73 - flex: 1; 74 - position: relative; 75 - } 76 - 77 41 &__edit-button { 78 42 position: absolute; 79 43 top: 5px; ··· 88 52 } 89 53 } 90 54 91 - &__half-box { 55 + &__row { 92 56 display: flex; 57 + flex-direction: column; 58 + padding-top: 5px; 59 + padding-bottom: 10px; 60 + 61 + &--half { 62 + flex-direction: row; 63 + } 64 + 65 + &--value-overflow { 66 + padding-bottom: 0; 67 + min-height: 0; 68 + } 93 69 } 94 70 95 71 &__half-entry { 96 - flex: 1; 97 - max-width: 50%; 72 + flex: none; 73 + width: 50%; 98 74 } 99 75 100 76 &__header { ··· 103 79 font-weight: bold; 104 80 font-style: normal; 105 81 padding: 0; 106 - margin: @header-margin--top 0 @header-margin--bottom; 82 + margin: 0 0 5px; 107 83 } 108 84 109 85 &__link { 110 86 overflow-wrap: break-word; 87 + } 88 + 89 + &__value-overflow { 90 + min-height: 0; 91 + max-height: @max-height; 92 + flex: 1; 93 + 94 + overflow-y: auto; 95 + padding-bottom: 10px; 111 96 } 112 97 }
-5
resources/css/bem/beatmapset-stats.less
··· 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 4 .beatmapset-stats { 5 - color: #fff; 6 5 font-size: 12px; 7 - 8 - @media @desktop { 9 - width: @beatmapset-float-box-width; 10 - } 11 6 12 7 &__elapsed-bar { 13 8 position: absolute;
+2
resources/css/bem/form-select.less
··· 11 11 font-weight: initial; 12 12 line-height: normal; 13 13 margin: 0; 14 + min-width: 0; 14 15 15 16 display: flex; 16 17 align-items: baseline; ··· 50 51 flex: 1; 51 52 background-color: inherit; 52 53 border-radius: inherit; 54 + max-width: 100%; 53 55 } 54 56 }
+5
resources/css/bem/forum-post.less
··· 62 62 padding-right: @gutter-v2-desktop; 63 63 } 64 64 65 + &--blocked { 66 + padding-top: @_padding-content; 67 + font-size: @font-size--title-small; 68 + } 69 + 65 70 &--footer { 66 71 color: @osu-colour-c2; 67 72 }
+19
resources/css/bem/imagemap.less
··· 1 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 + // See the LICENCE file in the repository root for full licence text. 3 + 4 + .imagemap { 5 + position: relative; 6 + overflow: hidden; 7 + 8 + &__image { 9 + width: auto; 10 + height: auto; 11 + object-fit: contain; 12 + max-width: 100%; 13 + } 14 + 15 + &__link { 16 + display: block; 17 + position: absolute; 18 + } 19 + }
-6
resources/css/bem/mp-history-player-score.less
··· 109 109 font-weight: 700; 110 110 111 111 padding-right: 20px; 112 - 113 - display: none; 114 - 115 - @media (min-width: @width) { 116 - display: initial; 117 - } 118 112 } 119 113 120 114 &__mods {
+3 -3
resources/css/bem/news-post-preview.less
··· 30 30 } 31 31 32 32 &__post-date { 33 - flex: 0 0 60px; 33 + flex: none; 34 + width: 70px; 34 35 border-right: 1px solid @osu-colour-l1; 35 36 margin: 10px; 36 37 display: flex; ··· 38 39 align-items: flex-end; 39 40 color: @osu-colour-l1; 40 41 padding-right: 10px; 41 - overflow: hidden; 42 42 text-align: right; 43 43 44 44 .@{top}--collapsed & { ··· 46 46 justify-content: flex-end; 47 47 align-items: baseline; 48 48 margin: 0px 10px; 49 - padding: 5px 10px; 49 + padding: 5px 10px 5px 0; 50 50 } 51 51 } 52 52
+3 -3
resources/css/bem/page-mode.less
··· 37 37 38 38 &--profile-page-extra { 39 39 .default-gutter-v2(); 40 - padding-top: 0; 41 - padding-bottom: 0; 42 - height: 40px; 40 + padding-top: 10px; 41 + padding-bottom: $padding-top; 43 42 display: flex; 44 43 align-items: center; 45 44 gap: 20px; 46 45 font-size: @font-size--title-small; 46 + overflow-x: auto; 47 47 } 48 48 49 49 &--ranking {
+1
resources/css/bem/search-entry.less
··· 10 10 padding: 10px 10px 15px; 11 11 position: relative; // for entries which link is full-sized child element 12 12 box-shadow: inset 0 0 0 var(--border-size) var(--border-colour); 13 + min-width: 0; 13 14 --border-colour: hsl(var(--hsl-l1)); 14 15 --border-size: 0; 15 16
+3 -2
resources/css/bem/simple-form.less
··· 204 204 } 205 205 206 206 &__row { 207 - flex: 1 0 100%; 207 + flex: 1 0 auto; 208 + width: 100%; 208 209 display: flex; 209 210 margin-bottom: 10px; 210 211 text-transform: none; ··· 225 226 } 226 227 227 228 &--half { 228 - flex-basis: 50%; 229 + flex: 50%; 229 230 } 230 231 231 232 &--title {
+2
resources/css/bem/supporter-quote.less
··· 21 21 margin: 0; 22 22 padding: 0; 23 23 border: none; 24 + // avoid right quote mark from overlapping the content in rare case 25 + position: relative; 24 26 } 25 27 26 28 &__quote-mark {
+1 -51
resources/js/beatmap-discussions-history/main.coffee
··· 33 33 relatedDiscussions: props.relatedDiscussions 34 34 35 35 36 - componentDidMount: => 37 - $.subscribe "beatmapsetDiscussions:update.#{@eventId}", @discussionUpdate 38 - $(document).on "ajax:success.#{@eventId}", '.js-beatmapset-discussion-update', @ujsDiscussionUpdate 39 - 40 - 41 36 componentWillUnmount: => 42 - $.unsubscribe ".#{@eventId}" 43 - $(window).off ".#{@eventId}" 44 - 45 37 $(window).stop() 46 38 47 39 48 - discussionUpdate: (_e, options) => 49 - {beatmapset} = options 50 - return unless beatmapset? 51 - 52 - discussions = [@state.discussions...] 53 - users = [@state.users...] 54 - relatedDiscussions = [@state.relatedDiscussions...] 55 - 56 - discussionIds = _.map discussions, 'id' 57 - userIds = _.map users, 'id' 58 - 59 - # Due to the entire hierarchy of discussions being sent back when a post is updated (instead of just the modified post), 60 - # we need to iterate over each discussion and their posts to extract the updates we want. 61 - _.each beatmapset.discussions, (newDiscussion) -> 62 - if discussionIds.includes(newDiscussion.id) 63 - discussion = _.find discussions, id: newDiscussion.id 64 - discussions = _.reject discussions, id: newDiscussion.id 65 - newDiscussion = _.merge(discussion, newDiscussion) 66 - # The discussion list shows discussions started by the current user, so it can be assumed that the first post is theirs 67 - newDiscussion.starting_post = newDiscussion.posts[0] 68 - discussions.push(newDiscussion) 69 - else 70 - relatedDiscussions.push(newDiscussion) 71 - 72 - _.each beatmapset.related_users, (newUser) -> 73 - if userIds.includes(newUser.id) 74 - users = _.reject users, id: newUser.id 75 - 76 - users.push(newUser) 77 - 78 - @cache.users = @cache.discussions = @cache.beatmaps = @cache.beatmapsets = @state.relatedDiscussions = null 79 - @setState 80 - discussions: _.reverse(_.sortBy(discussions, (d) -> Date.parse(d.starting_post.created_at))) 81 - users: users 82 - relatedDiscussions: relatedDiscussions 83 - 84 - 85 40 discussions: => 86 41 # skipped discussions 87 42 # - not privileged (deleted discussion) ··· 136 91 currentUser: currentUser 137 92 beatmapset: beatmapsets[discussion.beatmapset_id] 138 93 isTimelineVisible: false 139 - visible: false 94 + readonly: true 140 95 showDeleted: true 141 96 preview: true 142 97 ··· 147 102 @cache.users[null] = @cache.users[undefined] = deletedUser.toJson() 148 103 149 104 @cache.users 150 - 151 - 152 - ujsDiscussionUpdate: (_e, data) => 153 - # to allow ajax:complete to be run 154 - Timeout.set 0, => @discussionUpdate(null, beatmapset: data)
+3
resources/js/beatmap-discussions/discussion.tsx
··· 31 31 currentBeatmap: BeatmapExtendedJson | null; 32 32 isTimelineVisible: boolean; 33 33 parentDiscussion?: BeatmapsetDiscussionJson | null; 34 + readonly: boolean; 34 35 readPostIds?: Set<number>; 35 36 showDeleted: boolean; 36 37 users: Partial<Record<number | string, UserJson>>; ··· 66 67 static contextType = DiscussionsStateContext; 67 68 static defaultProps = { 68 69 preview: false, 70 + readonly: false, 69 71 }; 70 72 71 73 declare context: React.ContextType<typeof DiscussionsStateContext>; ··· 227 229 discussion={this.props.discussion} 228 230 post={post} 229 231 read={this.isRead(post)} 232 + readonly={this.props.readonly} 230 233 resolvedSystemPostId={this.resolvedSystemPostId} 231 234 type={type} 232 235 user={user}
+1 -1
resources/js/beatmap-discussions/editor-discussion-component.tsx
··· 264 264 const classMods = canEdit ? [] : ['read-only']; 265 265 266 266 const timestampTooltipType = this.props.element.beatmapId != null ? 'diff' : 'all-diff'; 267 - 268 267 const timestampTooltip = trans(`beatmaps.discussions.review.embed.timestamp.${timestampTooltipType}`, { 268 + // TODO: remove after translations are updated without the key 269 269 type: trans(`beatmaps.discussions.message_type.${this.discussionType()}`), 270 270 }); 271 271
+7 -2
resources/js/beatmap-discussions/nominations.coffee
··· 17 17 import { onError } from 'utils/ajax' 18 18 import { canModeratePosts, makeUrl } from 'utils/beatmapset-discussion-helper' 19 19 import { nominationsCount } from 'utils/beatmapset-helper' 20 - import { joinComponents, trans } from 'utils/lang' 20 + import { joinComponents, trans, transExists } from 'utils/lang' 21 21 import { hideLoadingOverlay, showLoadingOverlay } from 'utils/loading-overlay' 22 22 import { pageChange } from 'utils/page-change' 23 23 import { presence } from 'utils/string' ··· 296 296 rankingETA = @props.beatmapset.nominations.ranking_eta 297 297 date = 298 298 if rankingETA? 299 - moment(rankingETA).format(dateFormat) 299 + # TODO: remove after translations are updated 300 + if transExists 'beatmaps.nominations.rank_estimate.on' 301 + trans 'beatmaps.nominations.rank_estimate.on', 302 + date: moment(rankingETA).format(dateFormat) 303 + else 304 + moment(rankingETA).format(dateFormat) 300 305 else 301 306 trans 'beatmaps.nominations.rank_estimate.soon' 302 307
+50 -37
resources/js/beatmap-discussions/post.tsx
··· 43 43 discussion: BeatmapsetDiscussionJson; 44 44 post: BeatmapsetDiscussionMessagePostJson; 45 45 read: boolean; 46 + readonly: boolean; 46 47 resolvedSystemPostId: number; 47 48 type: string; 48 49 user: UserJson; ··· 69 70 && this.props.post.id > this.props.resolvedSystemPostId 70 71 && !this.props.beatmapset.discussion_locked 71 72 ); 73 + } 74 + 75 + private get canDelete() { 76 + return this.props.type === 'discussion' 77 + ? this.props.discussion.current_user_attributes?.can_destroy 78 + : this.canModerate || this.canEdit; 79 + } 80 + 81 + private get canModerate() { 82 + return canModeratePosts(); 72 83 } 73 84 74 85 @computed ··· 358 369 359 370 360 371 private renderMessageViewerActions() { 361 - const canModerate = canModeratePosts(); 362 - const canDelete = this.props.type === 'discussion' ? this.props.discussion.current_user_attributes?.can_destroy : canModerate || this.canEdit; 363 - 364 372 return ( 365 373 <div className={`${bn}__actions`}> 366 374 <div className={`${bn}__actions-group`}> ··· 371 379 valueAsUrl 372 380 /> 373 381 </span> 374 - {this.canEdit && ( 375 - <button 376 - className={`${bn}__action ${bn}__action--button`} 377 - onClick={this.editStart} 378 - > 379 - {trans('beatmaps.discussions.edit')} 380 - </button> 381 - )} 382 382 383 - {this.deleteModel.deleted_at == null && canDelete && ( 384 - <a 385 - className={`js-beatmapset-discussion-update ${bn}__action ${bn}__action--button`} 386 - data-confirm={trans('common.confirmation')} 387 - data-method='DELETE' 388 - data-remote 389 - href={this.deleteHref('destroy')} 390 - > 391 - {trans('beatmaps.discussions.delete')} 392 - </a> 393 - )} 383 + {!this.props.readonly && ( 384 + <> 385 + {this.canEdit && ( 386 + <button 387 + className={`${bn}__action ${bn}__action--button`} 388 + onClick={this.editStart} 389 + > 390 + {trans('beatmaps.discussions.edit')} 391 + </button> 392 + )} 394 393 395 - {this.deleteModel.deleted_at != null && canModerate && ( 396 - <a 397 - className={`js-beatmapset-discussion-update ${bn}__action ${bn}__action--button`} 398 - data-confirm={trans('common.confirmation')} 399 - data-method='POST' 400 - data-remote 401 - href={this.deleteHref('restore')} 402 - > 403 - {trans('beatmaps.discussions.restore')} 404 - </a> 405 - )} 394 + {this.deleteModel.deleted_at == null && this.canDelete && ( 395 + <a 396 + className={`js-beatmapset-discussion-update ${bn}__action ${bn}__action--button`} 397 + data-confirm={trans('common.confirmation')} 398 + data-method='DELETE' 399 + data-remote 400 + href={this.deleteHref('destroy')} 401 + > 402 + {trans('beatmaps.discussions.delete')} 403 + </a> 404 + )} 406 405 407 - {this.props.type === 'discussion' && this.props.discussion.current_user_attributes?.can_moderate_kudosu && ( 408 - this.props.discussion.can_grant_kudosu 409 - ? this.renderKudosuAction('deny') 410 - : this.props.discussion.kudosu_denied && this.renderKudosuAction('allow') 406 + {this.deleteModel.deleted_at != null && this.canModerate && ( 407 + <a 408 + className={`js-beatmapset-discussion-update ${bn}__action ${bn}__action--button`} 409 + data-confirm={trans('common.confirmation')} 410 + data-method='POST' 411 + data-remote 412 + href={this.deleteHref('restore')} 413 + > 414 + {trans('beatmaps.discussions.restore')} 415 + </a> 416 + )} 417 + 418 + {this.props.type === 'discussion' && this.props.discussion.current_user_attributes?.can_moderate_kudosu && ( 419 + this.props.discussion.can_grant_kudosu 420 + ? this.renderKudosuAction('deny') 421 + : this.props.discussion.kudosu_denied && this.renderKudosuAction('allow') 422 + )} 423 + </> 411 424 )} 412 425 413 426 {this.canReport && (
+1 -1
resources/js/beatmaps/beatmapset-card-size-selector.tsx
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 - import { BeatmapsetCardSize } from 'components/beatmapset-panel'; 4 + import { BeatmapsetCardSize } from 'beatmapset-panel'; 5 5 import { computed, makeObservable } from 'mobx'; 6 6 import { observer } from 'mobx-react'; 7 7 import core from 'osu-core-singleton';
+1 -1
resources/js/beatmaps/search-content.tsx
··· 3 3 4 4 import BeatmapsetCardSizeSelector from 'beatmaps/beatmapset-card-size-selector'; 5 5 import VirtualListMeta from 'beatmaps/virtual-list-meta'; 6 - import BeatmapsetPanel, { beatmapsetCardSizes } from 'components/beatmapset-panel'; 6 + import BeatmapsetPanel, { beatmapsetCardSizes } from 'beatmapset-panel'; 7 7 import Img2x from 'components/img2x'; 8 8 import StringWithComponent from 'components/string-with-component'; 9 9 import { route } from 'laroute';
+2 -1
resources/js/beatmaps/search-sort.tsx
··· 73 73 74 74 private readonly renderField = (field: Sort) => { 75 75 const active = this.filters.searchSort.field === field; 76 + const arrow = active && this.filters.searchSort.order === 'asc' ? 'up' : 'down'; 76 77 77 78 return ( 78 79 <a ··· 84 85 > 85 86 {trans(`beatmaps.listing.search.sorting.${field}`)} 86 87 <span className='sort__item-arrow'> 87 - <i className={`fas fa-caret-${this.filters.searchSort.order === 'asc' ? 'up' : 'down'}`} /> 88 + <i className={`fas fa-caret-${arrow}`} /> 88 89 </span> 89 90 </a> 90 91 );
+90 -92
resources/js/beatmapsets-show/header.tsx
··· 92 92 93 93 return ( 94 94 <div className='beatmapset-header'> 95 - <div className='beatmapset-header__content'> 96 - <div className='beatmapset-header__cover'> 97 - <BeatmapsetCover 98 - beatmapset={this.controller.beatmapset} 99 - forceShowVisual // check already covered by parent component 100 - modifiers='full' 101 - size='cover' 102 - /> 103 - </div> 95 + <div className='beatmapset-header__cover'> 96 + <BeatmapsetCover 97 + beatmapset={this.controller.beatmapset} 98 + forceShowVisual // check already covered by parent component 99 + modifiers='full' 100 + size='cover' 101 + /> 102 + </div> 104 103 105 - <div className='beatmapset-header__box beatmapset-header__box--main'> 106 - <div className='beatmapset-header__beatmap-picker-box'> 107 - <BeatmapPicker controller={this.controller} /> 104 + <div className='beatmapset-header__box beatmapset-header__box--main'> 105 + <div className='beatmapset-header__beatmap-picker-box'> 106 + <BeatmapPicker controller={this.controller} /> 108 107 109 - {this.renderBeatmapVersion()} 108 + {this.renderBeatmapVersion()} 110 109 111 - <div> 112 - <span className='beatmapset-header__value' title={trans('beatmapsets.show.stats.playcount')}> 113 - <span className='beatmapset-header__value-icon'><span className='fas fa-play-circle' /></span> 114 - <span className='beatmapset-header__value-name'>{formatNumber(this.controller.beatmapset.play_count)}</span> 115 - </span> 110 + <div> 111 + <span className='beatmapset-header__value' title={trans('beatmapsets.show.stats.playcount')}> 112 + <span className='beatmapset-header__value-icon'><span className='fas fa-play-circle' /></span> 113 + <span className='beatmapset-header__value-name'>{formatNumber(this.controller.beatmapset.play_count)}</span> 114 + </span> 116 115 117 - {this.controller.beatmapset.status === 'pending' && 118 - <span className='beatmapset-header__value' title={trans('beatmapsets.show.stats.nominations')}> 119 - <span className='beatmapset-header__value-icon'><span className='fas fa-thumbs-up' /></span> 120 - <span className='beatmapset-header__value-name'> 121 - {formatNumber(this.controller.beatmapset.nominations_summary.current)} 122 - </span> 123 - </span> 124 - } 125 - 126 - <span 127 - ref={this.favouriteIconRef} 128 - className={classWithModifiers('beatmapset-header__value', { 'has-favourites': this.controller.beatmapset.favourite_count > 0 })} 129 - onMouseOver={this.onEnterFavouriteIcon} 130 - onTouchStart={this.onEnterFavouriteIcon} 131 - > 132 - <span className='beatmapset-header__value-icon'> 133 - <span className='fas fa-heart' /> 134 - </span> 116 + {this.controller.beatmapset.status === 'pending' && 117 + <span className='beatmapset-header__value' title={trans('beatmapsets.show.stats.nominations')}> 118 + <span className='beatmapset-header__value-icon'><span className='fas fa-thumbs-up' /></span> 135 119 <span className='beatmapset-header__value-name'> 136 - {formatNumber(this.controller.beatmapset.favourite_count)} 120 + {formatNumber(this.controller.beatmapset.nominations_summary.current)} 137 121 </span> 138 122 </span> 139 - </div> 140 - </div> 123 + } 141 124 142 - <span className='beatmapset-header__details-text beatmapset-header__details-text--title'> 143 - <a 144 - className='beatmapset-header__details-text-link' 145 - href={route('beatmapsets.index', { q: getTitle(this.controller.beatmapset) })} 125 + <span 126 + ref={this.favouriteIconRef} 127 + className={classWithModifiers('beatmapset-header__value', { 'has-favourites': this.controller.beatmapset.favourite_count > 0 })} 128 + onMouseOver={this.onEnterFavouriteIcon} 129 + onTouchStart={this.onEnterFavouriteIcon} 146 130 > 147 - {getTitle(this.controller.beatmapset)} 148 - </a> 149 - <BeatmapsetBadge 150 - beatmapset={this.controller.beatmapset} 151 - type='nsfw' 152 - /> 153 - <BeatmapsetBadge 154 - beatmapset={this.controller.beatmapset} 155 - type='spotlight' 156 - /> 157 - </span> 131 + <span className='beatmapset-header__value-icon'> 132 + <span className='fas fa-heart' /> 133 + </span> 134 + <span className='beatmapset-header__value-name'> 135 + {formatNumber(this.controller.beatmapset.favourite_count)} 136 + </span> 137 + </span> 138 + </div> 139 + </div> 158 140 159 - <span className='beatmapset-header__details-text beatmapset-header__details-text--artist'> 160 - <a 161 - className='beatmapset-header__details-text-link' 162 - href={route('beatmapsets.index', { q: getArtist(this.controller.beatmapset) })} 163 - > 164 - {getArtist(this.controller.beatmapset)} 165 - </a> 166 - <BeatmapsetBadge 167 - beatmapset={this.controller.beatmapset} 168 - type='featured_artist' 169 - /> 170 - </span> 141 + <span className='beatmapset-header__details-text beatmapset-header__details-text--title'> 142 + <a 143 + className='beatmapset-header__details-text-link' 144 + href={route('beatmapsets.index', { q: getTitle(this.controller.beatmapset) })} 145 + > 146 + {getTitle(this.controller.beatmapset)} 147 + </a> 148 + <BeatmapsetBadge 149 + beatmapset={this.controller.beatmapset} 150 + type='nsfw' 151 + /> 152 + <BeatmapsetBadge 153 + beatmapset={this.controller.beatmapset} 154 + type='spotlight' 155 + /> 156 + </span> 171 157 172 - <BeatmapsetMapping beatmapset={this.controller.beatmapset} /> 158 + <span className='beatmapset-header__details-text beatmapset-header__details-text--artist'> 159 + <a 160 + className='beatmapset-header__details-text-link' 161 + href={route('beatmapsets.index', { q: getArtist(this.controller.beatmapset) })} 162 + > 163 + {getArtist(this.controller.beatmapset)} 164 + </a> 165 + <BeatmapsetBadge 166 + beatmapset={this.controller.beatmapset} 167 + type='featured_artist' 168 + /> 169 + </span> 170 + 171 + <BeatmapsetMapping beatmapset={this.controller.beatmapset} /> 173 172 174 - {this.renderAvailabilityInfo()} 173 + {this.renderAvailabilityInfo()} 175 174 176 - <div className='beatmapset-header__buttons'> 177 - {core.currentUser != null && 178 - <BigButton 179 - icon={favouriteButton.icon} 180 - modifiers={['beatmapset-header-square', `beatmapset-header-square-${favouriteButton.action}`]} 181 - props={{ 182 - onClick: this.onClickFavourite, 183 - title: trans(`beatmapsets.show.details.${favouriteButton.action}`), 184 - }} 185 - /> 186 - } 175 + <div className='beatmapset-header__buttons'> 176 + {core.currentUser != null && 177 + <BigButton 178 + icon={favouriteButton.icon} 179 + modifiers={['beatmapset-header-square', `beatmapset-header-square-${favouriteButton.action}`]} 180 + props={{ 181 + onClick: this.onClickFavourite, 182 + title: trans(`beatmapsets.show.details.${favouriteButton.action}`), 183 + }} 184 + /> 185 + } 187 186 188 - {this.renderDownloadButtons()} 189 - {this.renderLoginButton()} 187 + {this.renderDownloadButtons()} 188 + {this.renderLoginButton()} 190 189 191 - {!this.controller.beatmapset.is_scoreable && core.currentUser != null && core.currentUser.id !== this.controller.beatmapset.user_id && 192 - <div className='beatmapset-header__more'> 193 - <div className='btn-circle btn-circle--page-toggle btn-circle--page-toggle-detail'> 194 - <BeatmapsetMenu beatmapset={this.controller.beatmapset} /> 195 - </div> 190 + {!this.controller.beatmapset.is_scoreable && core.currentUser != null && core.currentUser.id !== this.controller.beatmapset.user_id && 191 + <div className='beatmapset-header__more'> 192 + <div className='btn-circle btn-circle--page-toggle btn-circle--page-toggle-detail'> 193 + <BeatmapsetMenu beatmapset={this.controller.beatmapset} /> 196 194 </div> 197 - } 198 - </div> 195 + </div> 196 + } 199 197 </div> 198 + </div> 200 199 201 - <div className='beatmapset-header__box beatmapset-header__box--stats'> 202 - {this.renderStatusBar()} 200 + <div className='beatmapset-header__box beatmapset-header__box--stats'> 201 + {this.renderStatusBar()} 203 202 204 - <Stats controller={this.controller} /> 205 - </div> 203 + <Stats controller={this.controller} /> 206 204 </div> 207 205 </div> 208 206 );
+17 -25
resources/js/beatmapsets-show/info.tsx
··· 85 85 render() { 86 86 const tags = this.controller.beatmapset.tags 87 87 .split(' ') 88 - .filter(present) 89 - .slice(0, 21); 90 - 91 - const tagsOverload = tags.length === 21; 92 - 93 - if (tagsOverload) { 94 - tags.pop(); 95 - } 88 + .filter(present); 96 89 97 90 return ( 98 - <div className='beatmapset-info'> 91 + <div className='beatmapset-info u-fancy-scrollbar'> 99 92 {this.isEditingDescription && 100 93 <Modal onClose={this.handleCloseDescriptionEditor}> 101 94 <div className='osu-page'> ··· 116 109 </Modal> 117 110 } 118 111 119 - <div className='beatmapset-info__box beatmapset-info__box--description'> 112 + <div className='beatmapset-info__box'> 120 113 {this.withEditDescription && this.renderEditDescriptionButton()} 121 114 122 - <h3 className='beatmapset-info__header'> 123 - {trans('beatmapsets.show.info.description')} 124 - </h3> 115 + <div className='beatmapset-info__row beatmapset-info__row--value-overflow'> 116 + <h3 className='beatmapset-info__header'> 117 + {trans('beatmapsets.show.info.description')} 118 + </h3> 125 119 126 - <div className='beatmapset-info__description-container u-fancy-scrollbar'> 127 120 <div 128 - className='beatmapset-info__description' 121 + className='beatmapset-info__value-overflow' 129 122 dangerouslySetInnerHTML={{ 130 123 __html: this.controller.beatmapset.description.description ?? '', 131 124 }} ··· 133 126 </div> 134 127 </div> 135 128 136 - <div className='beatmapset-info__box beatmapset-info__box--meta'> 129 + <div className='beatmapset-info__box'> 137 130 {this.withEditMetadata && this.renderEditMetadataButton()} 138 131 139 132 {this.nominators.length > 0 && 140 - <> 133 + <div className='beatmapset-info__row'> 141 134 <h3 className='beatmapset-info__header'> 142 135 {trans('beatmapsets.show.info.nominators')} 143 136 </h3> ··· 152 145 </React.Fragment> 153 146 ))} 154 147 </div> 155 - </> 148 + </div> 156 149 } 157 150 158 151 {present(this.controller.beatmapset.source) && 159 - <> 152 + <div className='beatmapset-info__row'> 160 153 <h3 className='beatmapset-info__header'> 161 154 {trans('beatmapsets.show.info.source')} 162 155 </h3> ··· 166 159 > 167 160 {this.controller.beatmapset.source} 168 161 </a> 169 - </> 162 + </div> 170 163 } 171 164 172 - <div className='beatmapset-info__half-box'> 165 + <div className='beatmapset-info__row beatmapset-info__row--half'> 173 166 <div className='beatmapset-info__half-entry'> 174 167 <h3 className='beatmapset-info__header'> 175 168 {trans('beatmapsets.show.info.genre')} ··· 196 189 </div> 197 190 198 191 {tags.length > 0 && 199 - <> 192 + <div className='beatmapset-info__row beatmapset-info__row--value-overflow'> 200 193 <h3 className='beatmapset-info__header'> 201 194 {trans('beatmapsets.show.info.tags')} 202 195 </h3> 203 - <div> 196 + <div className='beatmapset-info__value-overflow'> 204 197 {tags.map((tag, i) => ( 205 198 <React.Fragment key={`${tag}-${i}`}> 206 199 <a ··· 212 205 {' '} 213 206 </React.Fragment> 214 207 ))} 215 - {tagsOverload && '...'} 216 208 </div> 217 - </> 209 + </div> 218 210 } 219 211 </div> 220 212
+5 -5
resources/js/components/beatmapset-panel.tsx resources/js/beatmapset-panel/index.tsx
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 - import BeatmapsPopup from 'beatmapset-panel/beatmaps-popup'; 5 4 import BeatmapsetBadge from 'components/beatmapset-badge'; 6 5 import BeatmapsetCover from 'components/beatmapset-cover'; 7 6 import { CircularProgress } from 'components/circular-progress'; 7 + import StringWithComponent from 'components/string-with-component'; 8 + import TimeWithTooltip from 'components/time-with-tooltip'; 9 + import UserLink from 'components/user-link'; 8 10 import BeatmapJson from 'interfaces/beatmap-json'; 9 11 import BeatmapsetExtendedJson from 'interfaces/beatmapset-extended-json'; 10 12 import BeatmapsetJson, { BeatmapsetStatus } from 'interfaces/beatmapset-json'; ··· 22 24 import { formatNumber, formatNumberSuffixed } from 'utils/html'; 23 25 import { trans } from 'utils/lang'; 24 26 import { beatmapsetDownloadDirect } from 'utils/url'; 25 - import StringWithComponent from './string-with-component'; 26 - import TimeWithTooltip from './time-with-tooltip'; 27 - import UserLink from './user-link'; 27 + import BeatmapsPopup from './beatmaps-popup'; 28 28 29 29 export const beatmapsetCardSizes = ['normal', 'extra'] as const; 30 30 export type BeatmapsetCardSize = typeof beatmapsetCardSizes[number]; ··· 100 100 <span className='beatmapset-panel__stats-item-icon'> 101 101 <i className={`fa-fw ${icon}`} /> 102 102 </span> 103 - <span>{formatNumberSuffixed(value, undefined, { maximumFractionDigits: 1, minimumFractionDigits: 0 })}</span> 103 + <span>{formatNumberSuffixed(value)}</span> 104 104 </div> 105 105 ); 106 106
-196
resources/js/components/comment-editor.coffee
··· 1 - # Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 - # See the LICENCE file in the repository root for full licence text. 3 - 4 - import BigButton from './big-button' 5 - import { Spinner } from './spinner' 6 - import UserAvatar from './user-avatar' 7 - import { route } from 'laroute' 8 - import core from 'osu-core-singleton' 9 - import * as React from 'react' 10 - import TextareaAutosize from 'react-autosize-textarea' 11 - import { button, div, span } from 'react-dom-factories' 12 - import { onErrorWithCallback } from 'utils/ajax' 13 - import { classWithModifiers } from 'utils/css' 14 - import { InputEventType, makeTextAreaHandler } from 'utils/input-handler' 15 - import { trans } from 'utils/lang' 16 - 17 - el = React.createElement 18 - 19 - bn = 'comment-editor' 20 - 21 - export class CommentEditor extends React.PureComponent 22 - constructor: (props) -> 23 - super props 24 - 25 - @textarea = React.createRef() 26 - 27 - @handleKeyDown = makeTextAreaHandler @handleKeyDownCallback 28 - 29 - @state = 30 - message: @props.message ? '' 31 - posting: false 32 - 33 - 34 - componentDidMount: => 35 - if @props.focus ? true 36 - @textarea.current?.selectionStart = -1 37 - @textarea.current?.focus() 38 - 39 - 40 - componentWillUnmount: => 41 - @xhr?.abort() 42 - 43 - 44 - render: => 45 - mode = @mode() 46 - canComment = @canComment() 47 - 48 - placeholder = 49 - if mode in ['new', 'reply'] && !canComment 50 - @props.commentableMeta.current_user_attributes?.can_new_comment_reason ? trans('authorization.comment.store.disabled') 51 - else 52 - trans("comments.placeholder.#{mode}") 53 - 54 - blockClass = classWithModifiers bn, @props.modifiers, fancy: mode == 'new' 55 - 56 - div className: blockClass, 57 - if mode == 'new' 58 - div className: "#{bn}__avatar", 59 - el UserAvatar, user: currentUser, modifiers: ['full-circle'] 60 - 61 - el TextareaAutosize, 62 - className: "#{bn}__message" 63 - ref: @textarea 64 - value: @state.message 65 - placeholder: placeholder 66 - onChange: @onChange 67 - onKeyDown: @handleKeyDown 68 - disabled: !canComment || @state.posting 69 - div 70 - className: "#{bn}__footer" 71 - div className: "#{bn}__footer-item #{bn}__footer-item--notice hidden-xs", 72 - if canComment 73 - trans 'comments.editor.textarea_hint._', 74 - action: trans("comments.editor.textarea_hint.#{mode}") 75 - 76 - if @props.close? 77 - div className: "#{bn}__footer-item", 78 - el BigButton, 79 - disabled: @state.posting 80 - modifiers: 'comment-editor' 81 - props: 82 - onClick: @props.close 83 - text: trans('common.buttons.cancel') 84 - 85 - if currentUser.id? 86 - div className: "#{bn}__footer-item", 87 - el BigButton, 88 - disabled: @state.posting || !@isValid() 89 - isBusy: @state.posting 90 - modifiers: 'comment-editor' 91 - props: 92 - onClick: @post 93 - text: 94 - top: 95 - if @state.posting 96 - el Spinner, modifiers: 'center-inline' 97 - else 98 - @buttonText() 99 - else 100 - div className: "#{bn}__footer-item", 101 - el BigButton, 102 - extraClasses: ['js-user-link'] 103 - modifiers: 'comment-editor' 104 - text: trans("comments.guest_button.#{mode}") 105 - 106 - 107 - buttonText: => 108 - key = 109 - switch @mode() 110 - when 'reply' then 'reply' 111 - when 'edit' then 'save' 112 - when 'new' then 'post' 113 - 114 - trans("common.buttons.#{key}") 115 - 116 - 117 - canComment: => 118 - return false if !core.currentUser? 119 - 120 - if @mode() in ['new', 'reply'] 121 - @props.commentableMeta.current_user_attributes? && !@props.commentableMeta.current_user_attributes?.can_new_comment_reason? 122 - else 123 - true 124 - 125 - 126 - close: => 127 - return unless @props.close? 128 - 129 - initialMessage = @props.message ? '' 130 - 131 - return if initialMessage != @state.message && !confirm(trans('common.confirmation_unsaved')) 132 - 133 - @props.close() 134 - 135 - 136 - handleKeyDownCallback: (type, event) => 137 - switch type 138 - when InputEventType.Cancel 139 - @close() 140 - when InputEventType.Submit 141 - @post() 142 - 143 - 144 - isValid: => 145 - @state.message? && @state.message.length > 0 146 - 147 - 148 - mode: => 149 - if @props.parent? 150 - 'reply' 151 - else if @props.id? 152 - 'edit' 153 - else 154 - 'new' 155 - 156 - 157 - onChange: (e) => 158 - @setState message: e.target.value 159 - 160 - 161 - post: => 162 - return if @xhr? 163 - return @props.close?() if @mode() == 'edit' && @state.message == @props.message 164 - 165 - @setState posting: true 166 - 167 - data = comment: message: @state.message 168 - 169 - switch @mode() 170 - when 'reply', 'new' 171 - url = route 'comments.store' 172 - method = 'POST' 173 - data.comment.commentable_type = @props.commentableMeta.type 174 - data.comment.commentable_id = @props.commentableMeta.id 175 - data.comment.parent_id = @props.parent?.id 176 - 177 - onDone = (data) => 178 - @setState message: '' 179 - $.publish 'comments:new', data 180 - when 'edit' 181 - url = route 'comments.update', comment: @props.id 182 - method = 'PUT' 183 - 184 - onDone = (data) -> 185 - $.publish 'comment:updated', data 186 - 187 - @xhr = $.ajax url, {method, data} 188 - .always => 189 - @setState posting: false 190 - .done (data) => 191 - onDone(data) 192 - @props.onPosted?(@mode()) 193 - @props.close?() 194 - .fail onErrorWithCallback(@post) 195 - .always => 196 - @xhr = null
+269
resources/js/components/comment-editor.tsx
··· 1 + // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 + // See the LICENCE file in the repository root for full licence text. 3 + 4 + import { CommentableMetaJson } from 'interfaces/comment-json'; 5 + import { route } from 'laroute'; 6 + import { action, computed, makeObservable, observable, runInAction } from 'mobx'; 7 + import { observer } from 'mobx-react'; 8 + import { Comment } from 'models/comment'; 9 + import core from 'osu-core-singleton'; 10 + import * as React from 'react'; 11 + import TextareaAutosize from 'react-autosize-textarea'; 12 + import { onErrorWithCallback } from 'utils/ajax'; 13 + import { classWithModifiers, Modifiers } from 'utils/css'; 14 + import { InputEventType, makeTextAreaHandler, TextAreaCallback } from 'utils/input-handler'; 15 + import { trans } from 'utils/lang'; 16 + import { switchNever } from 'utils/switch-never'; 17 + import BigButton from './big-button'; 18 + import { Spinner } from './spinner'; 19 + import UserAvatar from './user-avatar'; 20 + 21 + type Mode = 'edit' | 'new' | 'reply'; 22 + 23 + interface CommentPostParams { 24 + comment: { 25 + commentable_id?: number; 26 + commentable_type?: string; 27 + message: string; 28 + parent_id?: number; 29 + }; 30 + } 31 + 32 + interface Props { 33 + close?: () => void; 34 + commentableMeta?: CommentableMetaJson; 35 + focus?: boolean; 36 + id?: number; 37 + message?: string; 38 + modifiers?: Modifiers; 39 + onPosted?: (mode: Mode) => void; 40 + parent?: Comment; 41 + } 42 + 43 + const bn = 'comment-editor'; 44 + 45 + const buttonTextKey: Record<Mode, string> = { 46 + edit: 'save', 47 + new: 'post', 48 + reply: 'reply', 49 + }; 50 + 51 + @observer 52 + export default class CommentEditor extends React.Component<Props> { 53 + private readonly handleKeyDown; 54 + @observable private message: string; 55 + @observable private posting = false; 56 + private readonly textarea = React.createRef<HTMLTextAreaElement>(); 57 + private xhr: JQuery.jqXHR<unknown> | null = null; 58 + 59 + @computed 60 + private get canComment() { 61 + if (core.currentUser == null) return false; 62 + 63 + return this.mode === 'edit' || this.canNewCommentReason == null; 64 + } 65 + 66 + private get canNewCommentReason() { 67 + return this.props.commentableMeta == null 68 + ? null 69 + : 'current_user_attributes' in this.props.commentableMeta 70 + ? this.props.commentableMeta.current_user_attributes.can_new_comment_reason 71 + : trans('authorization.comment.store.disabled'); 72 + } 73 + 74 + private get isValid() { 75 + return this.message.length > 0; 76 + } 77 + 78 + private get initialMessage() { 79 + return this.props.message ?? ''; 80 + } 81 + 82 + @computed 83 + private get mode() { 84 + return this.props.parent != null 85 + ? 'reply' 86 + : this.props.id != null 87 + ? 'edit' 88 + : 'new'; 89 + } 90 + 91 + @computed 92 + private get placeholder() { 93 + return this.canComment 94 + ? trans(`comments.placeholder.${this.mode}`) 95 + : (this.canNewCommentReason ?? undefined); 96 + } 97 + 98 + constructor(props: Props) { 99 + super(props); 100 + 101 + this.handleKeyDown = makeTextAreaHandler(this.handleKeyDownCallback); 102 + this.message = this.initialMessage; 103 + 104 + makeObservable(this); 105 + } 106 + 107 + componentDidMount() { 108 + if ((this.props.focus ?? true) && this.textarea.current != null) { 109 + this.textarea.current.selectionStart = -1; 110 + this.textarea.current.focus(); 111 + } 112 + } 113 + 114 + componentWillUnmount() { 115 + this.xhr?.abort(); 116 + } 117 + 118 + render() { 119 + const blockClass = classWithModifiers(bn, this.props.modifiers, { fancy: this.mode === 'new' }); 120 + 121 + return ( 122 + <div className={blockClass}> 123 + {this.mode === 'new' && 124 + <div className={`${bn}__avatar`}> 125 + <UserAvatar modifiers='full-circle' user={core.currentUser} /> 126 + </div> 127 + } 128 + 129 + <TextareaAutosize 130 + ref={this.textarea} 131 + className={`${bn}__message`} 132 + disabled={!this.canComment || this.posting} 133 + onChange={this.onChange} 134 + onKeyDown={this.handleKeyDown} 135 + placeholder={this.placeholder} 136 + value={this.message} 137 + /> 138 + <div className={`${bn}__footer`}> 139 + <div className={`${bn}__footer-item ${bn}__footer-item--notice hidden-xs`}> 140 + {this.canComment && trans('comments.editor.textarea_hint._', { 141 + action: trans(`comments.editor.textarea_hint.${this.mode}`), 142 + })} 143 + </div> 144 + 145 + {this.props.close != null && 146 + <div className={`${bn}__footer-item`}> 147 + <BigButton 148 + disabled={this.posting} 149 + modifiers='comment-editor' 150 + props={{ onClick: this.props.close }} 151 + text={trans('common.buttons.cancel')} 152 + /> 153 + </div> 154 + } 155 + 156 + {core.currentUser != null 157 + ? ( 158 + <div className={`${bn}__footer-item`}> 159 + <BigButton 160 + disabled={this.posting || !this.isValid} 161 + isBusy={this.posting} 162 + modifiers='comment-editor' 163 + props={{ onClick: this.post }} 164 + text={{ 165 + top: this.posting 166 + ? <Spinner modifiers='center-inline' /> 167 + : trans(`common.buttons.${buttonTextKey[this.mode]}`), 168 + }} 169 + /> 170 + </div> 171 + ) : ( 172 + <div className={`${bn}__footer-item`}> 173 + <BigButton 174 + extraClasses={['js-user-link']} 175 + modifiers='comment-editor' 176 + text={trans(`comments.guest_button.${this.mode}`)} 177 + /> 178 + </div> 179 + ) 180 + } 181 + </div> 182 + </div> 183 + ); 184 + } 185 + 186 + private readonly close = () => { 187 + if (this.props.close == null) return; 188 + 189 + if (this.initialMessage !== this.message && !confirm(trans('common.confirmation_unsaved'))) return; 190 + 191 + this.props.close(); 192 + }; 193 + 194 + private readonly handleKeyDownCallback: TextAreaCallback = (type) => { 195 + if (type === InputEventType.Cancel) { 196 + this.close(); 197 + } else if (type === InputEventType.Submit) { 198 + this.post(); 199 + } 200 + }; 201 + 202 + @action 203 + private readonly onChange = (e: React.KeyboardEvent<HTMLTextAreaElement>) => { 204 + this.message = e.currentTarget.value; 205 + }; 206 + 207 + private readonly post = () => { 208 + if (this.posting) return; 209 + 210 + if (this.mode === 'edit' && this.message === (this.props.message ?? '')) { 211 + this.props.close?.(); 212 + 213 + return; 214 + } 215 + 216 + this.posting = true; 217 + const params: CommentPostParams = { 218 + comment: { message: this.message }, 219 + }; 220 + 221 + let url = route('comments.store'); 222 + let method = 'POST'; 223 + let resetMessage = true; 224 + let publishEvent = 'comments:new'; 225 + 226 + switch (this.mode) { 227 + case 'edit': 228 + if (this.props.id == null) { 229 + throw new Error('missing post id in edit mode'); 230 + } 231 + url = route('comments.update', { comment: this.props.id }); 232 + method = 'PUT'; 233 + resetMessage = false; 234 + publishEvent = 'comment:updated'; 235 + break; 236 + 237 + case 'new': 238 + if (this.props.commentableMeta == null || !('id' in this.props.commentableMeta)) { 239 + throw new Error('missing commentable meta in new mode'); 240 + } 241 + params.comment.commentable_type = this.props.commentableMeta.type; 242 + params.comment.commentable_id = this.props.commentableMeta.id; 243 + break; 244 + 245 + case 'reply': 246 + if (this.props.parent == null) { 247 + throw new Error('missing parent in reply mode'); 248 + } 249 + params.comment.parent_id = this.props.parent.id; 250 + break; 251 + 252 + default: 253 + switchNever(this.mode); 254 + } 255 + 256 + this.xhr = $.ajax(url, { data: params, method }); 257 + this.xhr 258 + .always(action(() => { 259 + this.posting = false; 260 + })).done((data) => runInAction(() => { 261 + if (resetMessage) { 262 + this.message = ''; 263 + } 264 + $.publish(publishEvent, data); 265 + this.props.onPosted?.(this.mode); 266 + this.props.close?.(); 267 + })).fail(onErrorWithCallback(this.post)); 268 + }; 269 + }
+2 -2
resources/js/components/comment.coffee
··· 12 12 import { createClickCallback, formatNumberSuffixed } from 'utils/html' 13 13 import { trans, transChoice } from 'utils/lang' 14 14 import ClickToCopy from './click-to-copy' 15 - import { CommentEditor } from './comment-editor' 15 + import CommentEditor from './comment-editor' 16 16 import CommentShowMore from './comment-show-more' 17 17 import DeletedCommentsCount from './deleted-comments-count' 18 18 import { ReportReportable } from './report-reportable' ··· 443 443 onClick: @voteToggle 444 444 disabled: @state.postingVote || !@props.comment.canVote 445 445 span className: 'comment-vote__text', 446 - "+#{formatNumberSuffixed(@props.comment.votesCount, null, maximumFractionDigits: 1)}" 446 + "+#{formatNumberSuffixed(@props.comment.votesCount)}" 447 447 if @state.postingVote 448 448 span className: 'comment-vote__spinner', el Spinner 449 449 hover
+1 -1
resources/js/components/comments.coffee
··· 9 9 import { formatNumber } from 'utils/html' 10 10 import { trans } from 'utils/lang' 11 11 import { Comment } from './comment' 12 - import { CommentEditor } from './comment-editor' 12 + import CommentEditor from './comment-editor' 13 13 import CommentShowMore from './comment-show-more' 14 14 import { CommentsSort } from './comments-sort' 15 15 import DeletedCommentsCount from './deleted-comments-count'
+5 -1
resources/js/components/report-form.tsx
··· 82 82 @observable private selectedReason = this.options[0]; 83 83 private timeout: number | undefined; 84 84 85 + private get canSubmit() { 86 + return !this.disabled && this.comments.length > 0; 87 + } 88 + 85 89 private get groupKey() { 86 90 return reportableTypeToGroupKey[this.props.reportableType]; 87 91 } ··· 208 212 <div className={`${bn}__row ${bn}__row--buttons`}> 209 213 <button 210 214 className={`${bn}__button ${bn}__button--report`} 211 - disabled={this.disabled || this.comments.length === 0} 215 + disabled={!this.canSubmit} 212 216 onClick={this.handleSubmit} 213 217 type='button' 214 218 >
+1 -1
resources/js/core-legacy/forum-topic-reply.coffee
··· 71 71 $input = @$input() 72 72 73 73 currentInput = $input.val() 74 - data = "#{currentInput}\n\n#{data}" if currentInput 74 + data = "#{currentInput}\n#{data}" if currentInput 75 75 76 76 $input.val(data).trigger('input') 77 77 $input[0].selectionStart = data.length
+1 -1
resources/js/interfaces/user-preferences-json.ts
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 - import { BeatmapsetCardSize } from 'components/beatmapset-panel'; 4 + import { BeatmapsetCardSize } from 'beatmapset-panel'; 5 5 import { ViewMode } from 'components/user-card'; 6 6 import { Filter, SortMode } from 'components/user-list'; 7 7
+1
resources/js/modding-profile/discussions.tsx
··· 66 66 discussion={discussion} 67 67 isTimelineVisible={false} 68 68 preview 69 + readonly 69 70 showDeleted 70 71 users={this.props.users} 71 72 />
-50
resources/js/modding-profile/main.coffee
··· 66 66 componentDidMount: => 67 67 $.subscribe "user:update.#{@eventId}", @userUpdate 68 68 $.subscribe "profile:page:jump.#{@eventId}", @pageJump 69 - $.subscribe "beatmapsetDiscussions:update.#{@eventId}", @discussionUpdate 70 - $(document).on "ajax:success.#{@eventId}", '.js-beatmapset-discussion-update', @ujsDiscussionUpdate 71 69 $(window).on "scroll.#{@eventId}", @pageScan 72 70 73 71 pageChange() ··· 84 82 componentWillUnmount: => 85 83 $.unsubscribe ".#{@eventId}" 86 84 $(window).off ".#{@eventId}" 87 - $(document).off ".#{@eventId}" 88 85 89 86 $(window).stop() 90 87 Timeout.clear @modeScrollTimeout 91 88 @disposers.forEach (disposer) => disposer?() 92 - 93 - 94 - discussionUpdate: (_e, options) => 95 - {beatmapset} = options 96 - return unless beatmapset? 97 - 98 - discussions = @state.discussions 99 - posts = @state.posts 100 - users = @state.users 101 - 102 - discussionIds = _.map discussions, 'id' 103 - postIds = _.map posts, 'id' 104 - userIds = _.map users, 'id' 105 - 106 - # Due to the entire hierarchy of discussions being sent back when a post is updated (instead of just the modified post), 107 - # we need to iterate over each discussion and their posts to extract the updates we want. 108 - _.each beatmapset.discussions, (newDiscussion) -> 109 - if discussionIds.includes(newDiscussion.id) 110 - discussion = _.find discussions, id: newDiscussion.id 111 - discussions = _.reject discussions, id: newDiscussion.id 112 - newDiscussion = _.merge(discussion, newDiscussion) 113 - 114 - newDiscussion.starting_post = newDiscussion.posts[0] 115 - discussions.push(newDiscussion) 116 - 117 - _.each newDiscussion.posts, (newPost) -> 118 - if postIds.includes(newPost.id) 119 - post = _.find posts, id: newPost.id 120 - posts = _.reject posts, id: newPost.id 121 - posts.push(_.merge(post, newPost)) 122 - 123 - _.each beatmapset.related_users, (newUser) -> 124 - if userIds.includes(newUser.id) 125 - users = _.reject users, id: newUser.id 126 - 127 - users.push(newUser) 128 - 129 - @cache.users = @cache.discussions = @cache.userDiscussions = @cache.beatmaps = @cache.beatmapsets = null 130 - @setState 131 - discussions: _.reverse(_.sortBy(discussions, (d) -> Date.parse(d.starting_post.created_at))) 132 - posts: _.reverse(_.sortBy(posts, (p) -> Date.parse(p.created_at))) 133 - users: users 134 89 135 90 136 91 discussions: => ··· 354 309 @cache.userDiscussions = _.filter @state.discussions, (d) => d.user_id == @state.user.id 355 310 356 311 @cache.userDiscussions 357 - 358 - 359 - ujsDiscussionUpdate: (_e, data) => 360 - # to allow ajax:complete to be run 361 - Timeout.set 0, => @discussionUpdate(null, beatmapset: data)
+1 -5
resources/js/modding-profile/posts.coffee
··· 58 58 users: @props.users 59 59 user: @props.users[post.user_id] 60 60 read: true 61 + readonly: true 61 62 lastEditor: @props.users[post.last_editor_id] ? @props.users[null] if post.last_editor_id? 62 - # FIXME: These permissions are more restrictive than the correct ones in discussion 63 - # because they don't have the right data to check. 64 - canBeEdited: currentUser.is_admin 65 - canBeDeleted: canModerate 66 - canBeRestored: canModerate 67 63 currentUser: currentUser 68 64 a 69 65 key: 'show-more'
+33 -32
resources/js/notifications-index/main.tsx
··· 35 35 })); 36 36 } 37 37 38 + private get type() { 39 + return this.controller.type; 40 + } 41 + 38 42 constructor(props: Record<string, never>, context: NotificationContextData) { 39 43 super(props); 40 44 ··· 54 58 55 59 <div className='osu-page osu-page--generic-compact'> 56 60 <div className='notification-index'> 57 - <div className='notification-index__actions'> 58 - {this.renderMarkAsReadButton()} 59 - {this.renderDeleteButton()} 60 - </div> 61 + {!this.type.isEmpty && 62 + <div className='notification-index__actions'> 63 + {this.renderMarkAsReadButton()} 64 + {this.renderDeleteButton()} 65 + </div> 66 + } 61 67 62 68 {this.renderLegacyPm()} 63 69 64 - <div className='notification-stacks'> 65 - {this.renderStacks()} 66 - {this.renderShowMore()} 67 - </div> 70 + {this.type.isEmpty 71 + ? this.type.isLoading 72 + ? null 73 + : trans('notifications.none') 74 + : this.renderStacks() 75 + } 76 + 77 + {this.renderShowMore()} 68 78 </div> 69 79 </div> 70 80 </div> ··· 78 88 } 79 89 80 90 renderShowMore() { 81 - const type = this.controller.type; 82 - 83 91 return ( 84 92 <ShowMoreLink 85 93 callback={this.handleShowMore} 86 - hasMore={type?.hasMore} 87 - loading={type?.isLoading} 88 - modifiers={['notification-group', 'notification-list']} 94 + hasMore={this.type.hasMore} 95 + loading={this.type.isLoading} 96 + modifiers='notification-group' 89 97 /> 90 98 ); 91 99 } 92 100 93 101 renderStacks() { 94 - const nodes: React.ReactNode[] = []; 95 - for (const stack of this.controller.stacks) { 96 - nodes.push(<Stack key={stack.id} stack={stack} />); 97 - } 98 - 99 - return nodes; 102 + return ( 103 + <div className='notification-stacks'> 104 + {this.controller.stacks.map((stack) => ( 105 + <Stack key={stack.id} stack={stack} /> 106 + ))} 107 + </div> 108 + ); 100 109 } 101 110 102 111 private handleDelete = () => { 103 - this.controller.type.delete(); 112 + this.type.delete(); 104 113 }; 105 114 106 115 private handleLinkClick = (event: React.MouseEvent<HTMLAnchorElement>) => { ··· 119 128 }; 120 129 121 130 private renderDeleteButton() { 122 - const type = this.controller.type; 123 - 124 - if (type.isEmpty) return null; 125 - 126 131 return ( 127 132 <NotificationDeleteButton 128 - isDeleting={type.isDeleting} 133 + isDeleting={this.type.isDeleting} 129 134 onDelete={this.handleDelete} 130 - text={trans('notifications.delete', { type: trans(`notifications.action_type.${type.name ?? '_'}`) })} 135 + text={trans('notifications.delete', { type: trans(`notifications.action_type.${this.type.name ?? '_'}`) })} 131 136 /> 132 137 ); 133 138 } 134 139 135 140 private renderMarkAsReadButton() { 136 - const type = this.controller.type; 137 - 138 - if (type.isEmpty) return null; 139 - 140 141 return ( 141 142 <NotificationReadButton 142 - isMarkingAsRead={type.isMarkingAsRead} 143 + isMarkingAsRead={this.type.isMarkingAsRead} 143 144 onMarkAsRead={this.handleMarkAsRead} 144 - text={trans('notifications.mark_read', { type: trans(`notifications.action_type.${type.name ?? '_'}`) })} 145 + text={trans('notifications.mark_read', { type: trans(`notifications.action_type.${this.type.name ?? '_'}`) })} 145 146 /> 146 147 ); 147 148 }
+1 -1
resources/js/profile-page/beatmapsets.tsx
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 - import BeatmapsetPanel from 'components/beatmapset-panel'; 4 + import BeatmapsetPanel from 'beatmapset-panel'; 5 5 import LazyLoad from 'components/lazy-load'; 6 6 import ProfilePageExtraSectionTitle from 'components/profile-page-extra-section-title'; 7 7 import ShowMoreLink from 'components/show-more-link';
+20 -16
resources/js/profile-page/detail-bar.tsx
··· 34 34 userId={this.props.user.id} 35 35 /> 36 36 37 - {this.renderNonBotButtons()} 37 + {this.props.user.is_bot ? this.renderMessageButton() : this.renderNonBotButtons()} 38 38 </div> 39 39 ); 40 40 } 41 41 42 - private renderNonBotButtons() { 43 - if (this.props.user.is_bot) return null; 42 + private renderMessageButton() { 43 + if (!this.showMessageButton) return null; 44 44 45 45 return ( 46 + // extra div to allow using same user-action-button--profile-page 47 + // like other buttons without resorting to additional styling 48 + <div> 49 + <a 50 + className='user-action-button user-action-button--profile-page' 51 + href={route('messages.users.show', { user: this.props.user.id })} 52 + title={trans('users.card.send_message')} 53 + > 54 + <i className='fas fa-envelope' /> 55 + </a> 56 + </div> 57 + ); 58 + } 59 + 60 + private renderNonBotButtons() { 61 + return ( 46 62 <> 47 63 <FollowUserMappingButton 48 64 alwaysVisible ··· 52 68 userId={this.props.user.id} 53 69 /> 54 70 55 - {this.showMessageButton && 56 - // extra div to allow using same user-action-button--profile-page 57 - // like other buttons without resorting to additional styling 58 - <div> 59 - <a 60 - className='user-action-button user-action-button--profile-page' 61 - href={route('messages.users.show', { user: this.props.user.id })} 62 - title={trans('users.card.send_message')} 63 - > 64 - <i className='fas fa-envelope' /> 65 - </a> 66 - </div> 67 - } 71 + {this.renderMessageButton()} 68 72 69 73 {showExtraMenu(this.props.user) && <ExtraMenu user={this.props.user} />} 70 74
+2 -4
resources/js/profile-page/main.tsx
··· 97 97 } 98 98 99 99 private get stickyHeaderOffset() { 100 - return core.windowSize.isDesktop 101 - ? core.stickyHeader.headerHeight + (this.pagesOffset?.getBoundingClientRect().height ?? 0) 102 - : core.stickyHeader.headerHeight; 100 + return core.stickyHeader.headerHeight + (this.pagesOffset?.getBoundingClientRect().height ?? 0); 103 101 } 104 102 105 103 constructor(props: Props) { ··· 205 203 <Detail controller={this.controller} /> 206 204 </div> 207 205 208 - <div ref={this.pagesOffsetRef} className='hidden-xs page-extra-tabs'> 206 + <div ref={this.pagesOffsetRef} className='page-extra-tabs'> 209 207 {this.displayExtraTabs && 210 208 <div ref={this.tabs} className='page-mode page-mode--profile-page-extra'> 211 209 {this.displayedExtraPages.map((m) => (
+8 -2
resources/js/profile-page/parse-event.tsx
··· 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 4 import EventJson from 'interfaces/event-json'; 5 + import core from 'osu-core-singleton'; 5 6 import * as React from 'react'; 6 7 import { Modifiers } from 'utils/css'; 7 8 import { trans } from 'utils/lang'; ··· 47 48 }, 48 49 }; 49 50 50 - case 'beatmapsetDelete': 51 + case 'beatmapsetDelete': { 52 + const canView = core.currentUser != null && (core.currentUser.is_bng || core.currentUser.is_moderator); 53 + 51 54 return { 52 55 badge: <span className='far fa-trash-alt' />, 53 56 iconModifiers: 'danger', 54 57 mappings: { 55 - beatmapset: event.beatmapset.title, 58 + beatmapset: canView 59 + ? <a href={event.beatmapset.url}>{event.beatmapset.title}</a> 60 + : event.beatmapset.title, 56 61 }, 57 62 }; 63 + } 58 64 59 65 case 'beatmapsetRevive': 60 66 return {
+1 -1
resources/js/register-components.tsx
··· 1 1 // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 2 2 // See the LICENCE file in the repository root for full licence text. 3 3 4 + import BeatmapsetPanel, { Props as BeatmapsetPanelProps } from 'beatmapset-panel'; 4 5 import BeatmapsetEvents, { Props as BeatmapsetEventsProps } from 'components/beatmapset-events'; 5 - import BeatmapsetPanel, { Props as BeatmapsetPanelProps } from 'components/beatmapset-panel'; 6 6 import BlockButton from 'components/block-button'; 7 7 import ChatIcon from 'components/chat-icon'; 8 8 import { Comments } from 'components/comments';
+10 -19
resources/js/utils/html.ts
··· 7 7 8 8 const byteSuffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 9 9 const kilo = 1000; 10 - const numberSuffixes = ['', 'k', 'm', 'b', 't']; 11 10 12 11 export function bottomPage() { 13 12 return bottomPageDistance() === 0; ··· 85 84 return num.toLocaleString(locale ?? window.currentLocale, options); 86 85 } 87 86 88 - export function formatNumberSuffixed(num?: number, precision?: number, options?: Intl.NumberFormatOptions) { 89 - if (num == null) return; 90 - 91 - const format = (n: number) => { 92 - options ??= {}; 93 - 94 - if (precision != null) { 95 - options.minimumFractionDigits = precision; 96 - options.maximumFractionDigits = precision; 97 - } 98 - 99 - return n.toLocaleString('en', options); 100 - }; 101 - 102 - if (num < kilo) return format(num); 103 - 104 - const i = Math.min(numberSuffixes.length - 1, Math.floor(Math.log(num) / Math.log(kilo))); 87 + const defaultSuffixedNumberOptions = { 88 + maximumFractionDigits: 1, 89 + minimumFractionDigits: 0, 90 + notation: 'compact', 91 + } as const; 92 + const defaultSuffixedNumberFormatter = new Intl.NumberFormat(window.currentLocale, defaultSuffixedNumberOptions); 105 93 106 - return `${format(num / Math.pow(kilo, i))}${numberSuffixes[i]}`; 94 + export function formatNumberSuffixed(num?: number) { 95 + return num == null 96 + ? undefined 97 + : defaultSuffixedNumberFormatter.format(num); 107 98 } 108 99 109 100 export function htmlElementOrNull(thing: unknown) {
+1
resources/lang/ar/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'مجموعات خرائط محزومة-مسبقا مبنية على اساس موضوع مشهور.', 9 + 'empty' => '', 9 10 'nav_title' => 'القائمة', 10 11 'title' => 'حُزَم الخرائِط', 11 12
+1
resources/lang/ar/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'هذه الخريطة مقدرة بأن تصبح Ranked في :date إذا لم يتم العثور على أي مشاكل. انها#:position في :queue.', 202 + 'on' => '', 202 203 'queue' => 'قائمة انتظار الترتيب', 203 204 'soon' => 'قريبًا', 204 205 ],
+1
resources/lang/be/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Папярэднія запакаваныя калекцыі бітмап, створаныя на агульных тэмах.', 9 + 'empty' => '', 9 10 'nav_title' => 'спіс', 10 11 'title' => 'Пакет бітмап', 11 12
+1
resources/lang/be/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Гэтая карта cможа маць рэйтынг :date, калі праблем не знойдзена. Гэта #:position у :queue.', 202 + 'on' => '', 202 203 'queue' => 'чаргу ў рэйтынг', 203 204 'soon' => 'хутка', 204 205 ],
+1 -1
resources/lang/bg/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Този бийтмап е заключен за дискусии.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Не може да се променят метаданните на номиниран бийтмап. Свържете се с номинатор или NAT член ако смятате, че са зададени грешно.',
+2 -1
resources/lang/bg/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Предварително пакетирани колекции от бийтмапове, базирани на обща тема.', 9 + 'empty' => '', 9 10 'nav_title' => 'пакети', 10 11 'title' => 'Бийтмап пакети', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Автор / Албум', 36 37 'chart' => 'Под прожекторите', 37 - 'featured' => '', 38 + 'featured' => 'Представени автори', 38 39 'standard' => 'Стандартни', 39 40 'theme' => 'Тема', 40 41 ],
+1
resources/lang/bg/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Този бийтмап ще бъде класиран на :date ако не открием проблеми. Той е #:position на :queue.', 202 + 'on' => '', 202 203 'queue' => 'опашката', 203 204 'soon' => 'скоро', 204 205 ],
+2 -2
resources/lang/bg/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Форуми', 29 29 'latest_post' => 'Последна публикация', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Наистина ли искате да възстановите публикацията?', 49 49 'edited' => 'Последно редактирано от :user :when, общо :count_delimited път.|Последно редактирано от :user :when, общо :count_delimited пъти.', 50 50 'posted_at' => 'публикувано :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'публикувано от :username в :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Изтриване на публикация',
+7 -7
resources/lang/bg/notifications.php
··· 14 14 'verifying' => 'Моля, потвърдете сесията, за преглед на известия', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'всички', 18 + 'beatmapset' => 'бийтмапове', 19 + 'build' => 'промени', 20 + 'channel' => 'чат', 21 + 'forum_topic' => 'форум', 22 + 'news_post' => 'новини', 23 + 'user' => 'профил', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/bg/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Създадена', 36 36 'feature_votes' => 'Приоритет по звезди', 37 37 'new' => 'Последен отговор', 38 38 ],
resources/lang/ca-ES/accounts.php resources/lang/ca/accounts.php
resources/lang/ca-ES/admin.php resources/lang/ca/admin.php
resources/lang/ca-ES/api.php resources/lang/ca/api.php
resources/lang/ca-ES/artist.php resources/lang/ca/artist.php
resources/lang/ca-ES/authorization.php resources/lang/ca/authorization.php
resources/lang/ca-ES/bbcode.php resources/lang/ca/bbcode.php
resources/lang/ca-ES/beatmap_discussion_posts.php resources/lang/ca/beatmap_discussion_posts.php
resources/lang/ca-ES/beatmap_discussions.php resources/lang/ca/beatmap_discussions.php
+1
resources/lang/ca-ES/beatmappacks.php resources/lang/ca/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Col·leccions preempaquetades de beatmaps basades en un tema comú.', 9 + 'empty' => '', 9 10 'nav_title' => 'llistat', 10 11 'title' => 'Paquets de beatmaps', 11 12
+1
resources/lang/ca-ES/beatmaps.php resources/lang/ca/beatmaps.php
··· 200 200 201 201 'rank_estimate' => [ 202 202 '_' => 'S\'estima que aquest mapa es classificarà en :date si no es troben problemes. És el número :position a la :queue.', 203 + 'on' => '', 203 204 'queue' => 'cua de classificació', 204 205 'soon' => 'aviat', 205 206 ],
resources/lang/ca-ES/beatmapset_discussion_votes.php resources/lang/ca/beatmapset_discussion_votes.php
resources/lang/ca-ES/beatmapset_events.php resources/lang/ca/beatmapset_events.php
resources/lang/ca-ES/beatmapset_watches.php resources/lang/ca/beatmapset_watches.php
resources/lang/ca-ES/beatmapsets.php resources/lang/ca/beatmapsets.php
resources/lang/ca-ES/changelog.php resources/lang/ca/changelog.php
resources/lang/ca-ES/chat.php resources/lang/ca/chat.php
resources/lang/ca-ES/client_verifications.php resources/lang/ca/client_verifications.php
resources/lang/ca-ES/comments.php resources/lang/ca/comments.php
resources/lang/ca-ES/common.php resources/lang/ca/common.php
resources/lang/ca-ES/community.php resources/lang/ca/community.php
resources/lang/ca-ES/contest.php resources/lang/ca/contest.php
resources/lang/ca-ES/errors.php resources/lang/ca/errors.php
resources/lang/ca-ES/events.php resources/lang/ca/events.php
resources/lang/ca-ES/follows.php resources/lang/ca/follows.php
resources/lang/ca-ES/forum.php resources/lang/ca/forum.php
resources/lang/ca-ES/friends.php resources/lang/ca/friends.php
resources/lang/ca-ES/help.php resources/lang/ca/help.php
resources/lang/ca-ES/home.php resources/lang/ca/home.php
resources/lang/ca-ES/layout.php resources/lang/ca/layout.php
resources/lang/ca-ES/livestreams.php resources/lang/ca/livestreams.php
resources/lang/ca-ES/mail.php resources/lang/ca/mail.php
resources/lang/ca-ES/matches.php resources/lang/ca/matches.php
resources/lang/ca-ES/model_validation.php resources/lang/ca/model_validation.php
resources/lang/ca-ES/model_validation/fulfillments.php resources/lang/ca/model_validation/fulfillments.php
resources/lang/ca-ES/model_validation/payments.php resources/lang/ca/model_validation/payments.php
resources/lang/ca-ES/model_validation/store/product.php resources/lang/ca/model_validation/store/product.php
resources/lang/ca-ES/multiplayer.php resources/lang/ca/multiplayer.php
resources/lang/ca-ES/news.php resources/lang/ca/news.php
resources/lang/ca-ES/notifications.php resources/lang/ca/notifications.php
resources/lang/ca-ES/oauth.php resources/lang/ca/oauth.php
resources/lang/ca-ES/page_title.php resources/lang/ca/page_title.php
resources/lang/ca-ES/password_reset.php resources/lang/ca/password_reset.php
resources/lang/ca-ES/paypal/errors.php resources/lang/ca/paypal/errors.php
resources/lang/ca-ES/quick_search.php resources/lang/ca/quick_search.php
resources/lang/ca-ES/rankings.php resources/lang/ca/rankings.php
resources/lang/ca-ES/report.php resources/lang/ca/report.php
resources/lang/ca-ES/scores.php resources/lang/ca/scores.php
resources/lang/ca-ES/sessions.php resources/lang/ca/sessions.php
resources/lang/ca-ES/sort.php resources/lang/ca/sort.php
resources/lang/ca-ES/store.php resources/lang/ca/store.php
resources/lang/ca-ES/supporter_tag.php resources/lang/ca/supporter_tag.php
resources/lang/ca-ES/tournament.php resources/lang/ca/tournament.php
resources/lang/ca-ES/user_verification.php resources/lang/ca/user_verification.php
resources/lang/ca-ES/users.php resources/lang/ca/users.php
resources/lang/ca-ES/validation.php resources/lang/ca/validation.php
resources/lang/ca-ES/wiki.php resources/lang/ca/wiki.php
+1
resources/lang/cs/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Kolekce beatmap s podobnou tématikou.', 9 + 'empty' => '', 9 10 'nav_title' => 'seznam', 10 11 'title' => 'Balíčky beatmap', 11 12
+1
resources/lang/cs/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Odhaduje se, že tato mapa bude schválena :date, pokud nejsou nalezeny žádné problémy. Aktuálně je #:position ve :queue.', 202 + 'on' => '', 202 203 'queue' => 'frontě schválení', 203 204 'soon' => 'brzy', 204 205 ],
+2 -2
resources/lang/cs/notifications.php
··· 15 15 16 16 'action_type' => [ 17 17 '_' => '', 18 - 'beatmapset' => '', 18 + 'beatmapset' => 'beatmapy', 19 19 'build' => '', 20 20 'channel' => '', 21 21 'forum_topic' => '', 22 22 'news_post' => '', 23 - 'user' => '', 23 + 'user' => 'profil', 24 24 ], 25 25 26 26 'filters' => [
+1
resources/lang/da/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Færdigpakkede samlinger af beatmaps bygget op omkring et fælles tema.', 9 + 'empty' => '', 9 10 'nav_title' => 'katalog', 10 11 'title' => 'Beatmap Pakker', 11 12
+1
resources/lang/da/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Dette kort anslås at være rangeret :date , hvis ingen problemer findes. Det er #:position i :queue.', 202 + 'on' => '', 202 203 'queue' => 'rangering kø', 203 204 'soon' => 'snart', 204 205 ],
+6 -6
resources/lang/de/accounts.php
··· 6 6 return [ 7 7 'edit' => [ 8 8 'title_compact' => 'Einstellungen', 9 - 'username' => 'benutzername', 9 + 'username' => 'Benutzername', 10 10 11 11 'avatar' => [ 12 12 'title' => 'Avatar', ··· 15 15 ], 16 16 17 17 'email' => [ 18 - 'new' => 'neue e-mail', 18 + 'new' => 'Neue E-Mail', 19 19 'new_confirmation' => 'E-Mail bestätigen', 20 20 'title' => 'E-Mail', 21 21 ], ··· 23 23 'password' => [ 24 24 'current' => 'Aktuelles Passwort', 25 25 'new' => 'neues passwort', 26 - 'new_confirmation' => 'Passwort bestätigen', 26 + 'new_confirmation' => 'passwort bestätigen', 27 27 'title' => 'Passwort', 28 28 ], 29 29 ··· 32 32 33 33 'user' => [ 34 34 'user_discord' => '', 35 - 'user_from' => 'aktueller standort', 35 + 'user_from' => 'Aktueller Standort', 36 36 'user_interests' => 'Interessen', 37 37 'user_occ' => 'Beschäftigung', 38 38 'user_twitter' => '', 39 - 'user_website' => 'Webseite', 39 + 'user_website' => 'webseite', 40 40 ], 41 41 ], 42 42 ··· 82 82 '_' => 'Bevorzugter Betmap-Download Typ', 83 83 'all' => 'mit Video, falls verfügbar', 84 84 'direct' => 'in osu!direct öffnen', 85 - 'no_video' => 'ohne Video', 85 + 'no_video' => 'ohne video', 86 86 ], 87 87 ], 88 88
+2 -2
resources/lang/de/artist.php
··· 18 18 ], 19 19 20 20 'index' => [ 21 - 'description' => 'Featured Artists sind Künstler, mit denen wir zusammenarbeiten, um neue und einzigartige Musik für osu! zur Verfügung zu stellen. Ihrer Lieder wurden vom osu!-Team handverlesen, weil sie großartige Werke sind und sich wunderbar für das Mapping eignen. Einige dieser Lieder wurden sogar exklusiv für osu! geschaffen.<br><br>Für alle Lieder steht eine Vorlage mit fertigem Timing als .osz Datei zum Download bereit. Außerdem sind sie offiziell für die Verwendung in osu! und für dazu in Bezug stehenden Inhalten lizenziert.', 21 + 'description' => 'Featured Artists sind Künstler, mit denen wir zusammenarbeiten, um neue und einzigartige Musik für osu! zur Verfügung zu stellen. Ihre Lieder wurden vom osu!-Team handverlesen, weil sie großartige Werke sind und sich wunderbar für das Mapping eignen. Einige dieser Lieder wurden sogar exklusiv für osu! geschaffen.<br><br>Für alle Lieder steht eine Vorlage mit fertigem Timing als .osz-Datei zum Download bereit. Außerdem sind sie offiziell für die Verwendung in osu! und für dazu in Bezug stehenden Inhalten lizenziert.', 22 22 ], 23 23 24 24 'links' => [ ··· 29 29 30 30 'songs' => [ 31 31 '_' => 'Songs', 32 - 'count' => ':count_delimited Songs|:count_delimited Songs', 32 + 'count' => ':count_delimited Song|:count_delimited Songs', 33 33 'original' => 'osu! original', 34 34 'original_badge' => 'ORIGINAL', 35 35 ],
+2 -2
resources/lang/de/authorization.php
··· 20 20 'exhausted' => 'Dein Nominierungslimit für heute wurde erreicht, bitte versuche es morgen erneut.', 21 21 'incorrect_state' => 'Beim Ausführen dieser Aktion ist ein Fehler aufgetreten. Bitte Seite neu laden.', 22 22 'owner' => "Eigene Beatmaps können nicht nominiert werden.", 23 - 'set_metadata' => 'Du musst den Genre und die Sprache einstellen, bevor nominiert werden kann.', 23 + 'set_metadata' => 'Vor der Nominierung müssen Genre und Sprache festgelegt werden.', 24 24 ], 25 25 'resolve' => [ 26 26 'not_owner' => 'Nur der Thread- oder Beatmapersteller kann die Diskussion für gelöst erklären.', ··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Diese Beatmap-Diskussion ist gesperrt.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Du kannst die Metadaten einer nominierten Map nicht ändern. Wenn du glaubst, dass sie falsch sind, wende dich an ein BN- oder NAT-Mitglied.',
+2 -1
resources/lang/de/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Fertig gepackte Sammlungen an Beatmaps, die auf einem gemeinsamen Thema basieren.', 9 + 'empty' => '', 9 10 'nav_title' => 'Liste', 10 11 'title' => 'Beatmap-Packs', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Künstler/Album', 36 37 'chart' => 'Spotlights', 37 - 'featured' => '', 38 + 'featured' => 'Featured Artist', 38 39 'standard' => 'Standard', 39 40 'theme' => 'Thema', 40 41 ],
+49 -48
resources/lang/de/beatmaps.php
··· 11 11 ], 12 12 13 13 'discussions' => [ 14 - 'allow_kudosu' => 'kudosu erlauben', 14 + 'allow_kudosu' => 'Kudosu erlauben', 15 15 'beatmap_information' => 'Beatmap-Seite', 16 16 'delete' => 'löschen', 17 17 'deleted' => 'Von :editor gelöscht (:delete_time).', 18 - 'deny_kudosu' => 'kudosu verweigern', 18 + 'deny_kudosu' => 'Kudosu ablehnen', 19 19 'edit' => 'bearbeiten', 20 20 'edited' => 'Zuletzt bearbeitet von :editor (:update_time).', 21 - 'guest' => 'Gästeschwierigkeit von :user', 22 - 'kudosu_denied' => 'Dir wurde kudosu verweigert.', 23 - 'message_placeholder_deleted_beatmap' => 'Diese Schwierigkeitsstufe wurde gelöscht und kann nicht mehr diskutiert werden.', 21 + 'guest' => 'Guest-Difficulty von :user', 22 + 'kudosu_denied' => 'Kudosu-Annahme abgelehnt.', 23 + 'message_placeholder_deleted_beatmap' => 'Diese Difficulty wurde gelöscht und kann nicht mehr diskutiert werden.', 24 24 'message_placeholder_locked' => 'Die Diskussion für diese Beatmap wurde deaktiviert.', 25 - 'message_placeholder_silenced' => "Posten der Diskussion nicht möglich, während du stummgeschaltet bist.", 26 - 'message_type_select' => 'Kommentartyp auswählen', 25 + 'message_placeholder_silenced' => "Posten in der Diskussion nicht möglich, während du stummgeschaltet bist.", 26 + 'message_type_select' => 'Bemerkungs-Art auswählen', 27 27 'reply_notice' => 'Zum Antworten Enter drücken.', 28 28 'reply_placeholder' => 'Antwort hier eingeben', 29 - 'require-login' => 'Zum Beitragen oder Antworten bitte einloggen', 29 + 'require-login' => 'Zum Posten oder Antworten bitte einloggen', 30 30 'resolved' => 'Gelöst', 31 31 'restore' => 'wiederherstellen', 32 32 'show_deleted' => 'Gelöschte anzeigen', ··· 39 39 40 40 'empty' => [ 41 41 'empty' => 'Es gibt noch keine Diskussionen!', 42 - 'hidden' => 'Keine Diskussion entspricht dem ausgewählten Filter.', 42 + 'hidden' => 'Keine Diskussion passt zum ausgewählten Filter.', 43 43 ], 44 44 45 45 'lock' => [ ··· 50 50 51 51 'prompt' => [ 52 52 'lock' => 'Grund für die Sperrung', 53 - 'unlock' => 'Willst du wirklich freigeben?', 53 + 'unlock' => 'Willst du das wirklich entsperren?', 54 54 ], 55 55 ], 56 56 57 57 'message_hint' => [ 58 - 'in_general' => 'Dieser Beitrag wird den generellen Beatmapdiskussionen hinzugefügt. Um diese Beatmap zu modden, beginne die Nachricht mit einem Zeitstempel (z.B. 00:12:345).', 59 - 'in_timeline' => 'Um an mehreren Zeitpunkten zu modden, musst du mehrere Beiträge erstellen (ein Beitrag pro Timestamp).', 58 + 'in_general' => 'Dieser Post wird zu den generellen Beatmap-Diskussionen hinzugefügt. Beginne die Nachricht mit einem Zeitstempel (z.B. 00:12:345), um diese Beatmap zu modden.', 59 + 'in_timeline' => 'Du musst mehrere Posts erstellen, um an mehreren Zeitstempeln zu modden (ein Post pro Zeitstempel).', 60 60 ], 61 61 62 62 'message_placeholder' => [ 63 - 'general' => 'Hier tippen um auf General zu posten (:version)', 64 - 'generalAll' => 'Hier tippen um auf General zu posten (Alle Schwierigkeitsstufen)', 65 - 'review' => 'Tippe hier, um eine Rezension zu posten', 66 - 'timeline' => 'Hier tippen um auf die Timeline zu posten (:version)', 63 + 'general' => 'Hier schreiben, um in General zu posten (:version)', 64 + 'generalAll' => 'Hier schreiben, um in General zu posten (Alle Difficulties)', 65 + 'review' => 'Hier schreiben, um eine Kurzfassung zu posten', 66 + 'timeline' => 'Hier schreiben, um in der Timeline zu posten (:version)', 67 67 ], 68 68 69 69 'message_type' => [ ··· 74 74 'praise' => 'Lob', 75 75 'problem' => 'Problem', 76 76 'problem_warning' => 'Problem melden', 77 - 'review' => 'Bewertung', 77 + 'review' => 'Kurzfassung', 78 78 'suggestion' => 'Vorschlag', 79 79 ], 80 80 81 81 'mode' => [ 82 82 'events' => 'Verlauf', 83 83 'general' => 'Allgemein :scope', 84 - 'reviews' => 'Rezensionen', 84 + 'reviews' => 'Überblick', 85 85 'timeline' => 'Timeline', 86 86 'scopes' => [ 87 - 'general' => 'Diese Schwierigkeitsstufe', 88 - 'generalAll' => 'Alle Schwierigkeitsstufen', 87 + 'general' => 'Diese Difficulty', 88 + 'generalAll' => 'Alle Difficulties', 89 89 ], 90 90 ], 91 91 92 92 'new' => [ 93 93 'pin' => 'Anheften', 94 - 'timestamp' => 'Timestamp', 95 - 'timestamp_missing' => 'Strg-C im Editor und in deine Nachricht einfügen, um eine Timestamp hinzuzufügen!', 94 + 'timestamp' => 'Zeitstempel', 95 + 'timestamp_missing' => 'Drücke Strg+C im Editor und füge es in deine Nachricht ein, um einen Zeitstempel hinzuzufügen!', 96 96 'title' => 'Neue Diskussion', 97 - 'unpin' => 'Lösen', 97 + 'unpin' => 'Aufheben', 98 98 ], 99 99 100 100 'review' => [ 101 - 'new' => 'Neue Rezension', 101 + 'new' => 'Neue Kurzfassung', 102 102 'embed' => [ 103 103 'delete' => 'Löschen', 104 104 'missing' => '[DISKUSSION GELÖSCHT]', ··· 112 112 'insert-block' => [ 113 113 'paragraph' => 'absatz einfügen', 114 114 'praise' => 'lob einfügen', 115 - 'problem' => 'problem einfügen', 116 - 'suggestion' => 'vorschlag einfügen', 115 + 'problem' => 'Problem einfügen', 116 + 'suggestion' => 'Vorschlag einfügen', 117 117 ], 118 118 ], 119 119 120 120 'show' => [ 121 - 'title' => ':title, erstellt von :mapper', 121 + 'title' => ':title erstellt von :mapper', 122 122 ], 123 123 124 124 'sort' => [ ··· 139 139 140 140 'status-messages' => [ 141 141 'approved' => 'Diese Beatmap wurde am :date approved!', 142 - 'graveyard' => "Diese Beatmap wurde seit dem :date nicht mehr aktualisiert und deshalb begraben...", 142 + 'graveyard' => "Diese Beatmap wurde seit dem :date nicht mehr aktualisiert und wurde wahrscheinlich vom Ersteller aufgegeben...", 143 143 'loved' => 'Diese Beatmap wurde am :date loved!', 144 144 'ranked' => 'Diese Beatmap wurde am :date ranked!', 145 - 'wip' => 'Anmerkung: Diese Beatmap ist vom Ersteller als \'Work-In-Progress\' gekennzeichnet', 145 + 'wip' => 'Anmerkung: Diese Beatmap wurde vom Ersteller als Work-In-Progress gekennzeichnet.', 146 146 ], 147 147 148 148 'votes' => [ ··· 159 159 160 160 'hype' => [ 161 161 'button' => 'Beatmap hypen!', 162 - 'button_done' => 'Schon gehypt!', 162 + 'button_done' => 'Schon gehyped!', 163 163 'confirm' => "Sicher? Dies wird eins deiner letzten :n Hypes verwenden und kann nicht rückgängig gemacht werden.", 164 164 'explanation' => 'Hype diese Beatmap, um sie für Nominierungen und Ranking sichtbarer zu machen!', 165 - 'explanation_guest' => 'Einloggen und diese Beatmap hypen, um sie für Nominierungen und ranked sichtbarer zu machen!', 165 + 'explanation_guest' => 'Logge dich ein und hype diese Beatmap, um sie für Nominierungen und Ranking sichtbarer zu machen!', 166 166 'new_time' => "Um :new_time wirst du deinen nächsten Hype erhalten.", 167 167 'remaining' => 'Du hast noch :remaining Hypes übrig.', 168 168 'required_text' => 'Hype: :current/:required', 169 - 'section_title' => 'Hype Train', 169 + 'section_title' => 'Hype-Train', 170 170 'title' => 'Hype', 171 171 ], 172 172 ··· 176 176 177 177 'nominations' => [ 178 178 'delete' => 'Löschen', 179 - 'delete_own_confirm' => 'Bist du sicher? Die Beatmap wird gelöscht und du wirst auf dein Profil zurückgeleitet.', 180 - 'delete_other_confirm' => 'Bist du sicher? Die Beatmap wird gelöscht und du wirst auf das Profil zurückgeleitet.', 179 + 'delete_own_confirm' => 'Bist du sicher? Die Beatmap wird gelöscht und du wirst auf dein Profil weitergeleitet.', 180 + 'delete_other_confirm' => 'Bist du sicher? Die Beatmap wird gelöscht und du wirst auf das Profil weitergeleitet.', 181 181 'disqualification_prompt' => 'Grund für die Disqualifizierung?', 182 182 'disqualified_at' => 'Disqualifiziert :time_ago (:reason).', 183 - 'disqualified_no_reason' => 'kein grund angegeben', 183 + 'disqualified_no_reason' => 'keinen Grund angegeben', 184 184 'disqualify' => 'Disqualifizieren', 185 185 'incorrect_state' => 'Ein Fehler ist aufgetreten, versuche die Seite zu aktualisieren.', 186 - 'love' => 'Liebe', 187 - 'love_choose' => 'Schwierigkeitsstufe für Loved wählen', 186 + 'love' => 'Love', 187 + 'love_choose' => 'Difficulty für Loved wählen', 188 188 'love_confirm' => 'Liebst du diese Beatmap?', 189 189 'nominate' => 'Nominieren', 190 190 'nominate_confirm' => 'Diese Beatmap nominieren?', ··· 198 198 'unresolved_issues' => 'Es existieren noch Vorschläge/Probleme, die gelöst werden müssen.', 199 199 200 200 'rank_estimate' => [ 201 - '_' => 'Diese Map wird voraussichtlich (am) :date ranked. Sie befindet sich aktuell an Position :position der :queue.', 201 + '_' => 'Diese Map wird voraussichtlich am :date ranked. Sie befindet sich aktuell an Position :position der :queue.', 202 + 'on' => '', 202 203 'queue' => 'Warteschlange', 203 204 'soon' => 'bald', 204 205 ], 205 206 206 207 'reset_at' => [ 207 208 'nomination_reset' => 'Nominierungsprozess zurückgesetzt vor :time_ago von :user mit dem Problem :discussion (:message).', 208 - 'disqualify' => 'Disqualifiziert :time_ago von :user mit der Erstellung des Problems :discussion (:message).', 209 + 'disqualify' => 'Vor :time_ago disqualifiziert von :user mit einem neuen Problem :discussion (:message).', 209 210 ], 210 211 211 212 'reset_confirm' => [ 212 - 'disqualify' => 'Bist du sicher? Dadurch wird die Beatmap aus der Qualifizierung entfernt und der Nominierungsprozess zurückgesetzt.', 213 + 'disqualify' => 'Bist du sicher? Das wird die Beatmap von der Qualifiezierung entfernen und den Nominierungsprozess zurücksetzen.', 213 214 'nomination_reset' => 'Bist du dir sicher? Der Nominierungsprozess wird durch das neue Problem zurückgesetzt.', 214 215 'problem_warning' => 'Bist du sicher, dass du ein Problem in dieser Beatmap melden möchtest? Dies wird die Beatmap Nominators alarmieren.', 215 216 ], ··· 217 218 218 219 'listing' => [ 219 220 'search' => [ 220 - 'prompt' => 'stichwörter eingeben...', 221 + 'prompt' => 'Stichwörter eingeben...', 221 222 'login_required' => 'Melde dich an, um zu suchen.', 222 223 'options' => 'Mehr Suchoptionen', 223 - 'supporter_filter' => 'Filtern nach :filters benötigt ein aktives osu!supporter-Tag', 224 - 'not-found' => 'keine ergebnisse', 224 + 'supporter_filter' => 'Um nach :filters zu filtern, benötigt es ein aktives osu!supporter-Tag', 225 + 'not-found' => 'keine Ergebnisse', 225 226 'not-found-quote' => '... nope, nichts gefunden.', 226 227 'filters' => [ 227 228 'extra' => 'Extra', 228 - 'general' => 'Generell', 229 + 'general' => 'Allgemein', 229 230 'genre' => 'Genre', 230 231 'language' => 'Sprache', 231 232 'mode' => 'Modus', ··· 275 276 'leaderboard' => 'Hat Ranglisten', 276 277 'loved' => 'Loved', 277 278 'mine' => 'Meine Maps', 278 - 'pending' => 'Ausstehend & WIP', 279 + 'pending' => 'Ausstehend', 279 280 'wip' => 'WIP', 280 281 'qualified' => 'Qualifiziert', 281 282 'ranked' => 'Ranked', ··· 325 326 'unplayed' => 'Ungespielt', 326 327 ], 327 328 'extra' => [ 328 - 'video' => 'Hat Video', 329 - 'storyboard' => 'Hat Storyboard', 329 + 'video' => 'Hat ein Video', 330 + 'storyboard' => 'Hat ein Storyboard', 330 331 ], 331 332 'rank' => [ 332 333 'any' => 'Alle', ··· 340 341 'D' => '', 341 342 ], 342 343 'panel' => [ 343 - 'playcount' => 'Anzahl der Spiele: :count', 344 + 'playcount' => 'Anzahl der Plays: :count', 344 345 'favourites' => 'Favoriten: :count', 345 346 ], 346 347 'variant' => [
+2 -2
resources/lang/de/beatmapset_discussion_votes.php
··· 5 5 6 6 return [ 7 7 'index' => [ 8 - 'title' => 'Beatmapdiskussion: Abstimmung', 8 + 'title' => 'Beatmap-Diskussions-Stimmen', 9 9 ], 10 10 11 11 'item' => [ 12 - 'score' => 'Punktzahl', 12 + 'score' => 'Ergebnis', 13 13 ], 14 14 ];
+19 -19
resources/lang/de/beatmapset_events.php
··· 6 6 return [ 7 7 'event' => [ 8 8 'approve' => 'Approved.', 9 - 'beatmap_owner_change' => 'Besitzer der Schwierigkeit :beatmap wurde auf :new_user geändert.', 9 + 'beatmap_owner_change' => 'Besitzer der Difficulty :beatmap wurde zu :new_user geändert.', 10 10 'discussion_delete' => 'Ein Moderator hat die Diskussion :discussion gelöscht.', 11 11 'discussion_lock' => 'Die Diskussion für diese Beatmap wurde deaktiviert. (:text)', 12 12 'discussion_post_delete' => 'Ein Moderator hat einen Beitrag der Diskussion :discussion gelöscht.', 13 13 'discussion_post_restore' => 'Ein Moderator hat einen Beitrag der Diskussion :discussion wiederhergestellt.', 14 14 'discussion_restore' => 'Ein Moderator hat die Diskussion :discussion wiederhergestellt.', 15 15 'discussion_unlock' => 'Die Diskussion für diese Beatmap wurde aktiviert.', 16 - 'disqualify' => 'Von :user disqualifiziert mit der Begründung: :discussion (:text).', 17 - 'disqualify_legacy' => 'Von :user disqualifiziert mit der Begründung: :text.', 16 + 'disqualify' => 'Von :user disqualifiziert. Grund: :discussion (:text).', 17 + 'disqualify_legacy' => 'Von :user disqualifiziert. Grund: :text.', 18 18 'genre_edit' => 'Genre wurde von :old zu :new geändert.', 19 - 'issue_reopen' => 'Gelöstes Problem :discussion von :discussion_user durch :user wiedereröffnet.', 20 - 'issue_resolve' => 'Vorschlag/Problem :discussion als gelöst gekennzeichnet.', 21 - 'kudosu_allow' => 'Das kudosu-Verbot für Diskussion :discussion wurde entfernt.', 22 - 'kudosu_deny' => 'Diskussion :discussion wurde das kudosu verwehrt.', 19 + 'issue_reopen' => 'Gelöstes Problem :discussion von :discussion_user wurde durch :user wiedereröffnet.', 20 + 'issue_resolve' => 'Problem :discussion von :discussion_user wurde durch :user als gelöst gekennzeichnet.', 21 + 'kudosu_allow' => 'Kudosu-Ablehnung für die Diskussion :discussion wurde entfernt.', 22 + 'kudosu_deny' => 'Kudosu werden in der Diskussion :discussion abgelehnt.', 23 23 'kudosu_gain' => 'Die Diskussion :discussion von :user hat genug Stimmen für Kudosu erhalten.', 24 - 'kudosu_lost' => 'Die Diskussion :discussion von :user hat an Stimmen verloren und kudosu wurde entfernt.', 25 - 'kudosu_recalculate' => 'Das verteilte kudosu der Diskussion :discussion wurde neu berechnet.', 24 + 'kudosu_lost' => 'Die Diskussion :discussion von :user hat an Stimmen verloren und erteilte Kudosu wurden abgelehnt.', 25 + 'kudosu_recalculate' => 'Erteilte Kudosu der Diskussion :discussion wurden neu berechnet.', 26 26 'language_edit' => 'Sprache wurde von :old zu :new geändert.', 27 - 'love' => 'Nominiert von :user', 27 + 'love' => 'Loved von :user.', 28 28 'nominate' => 'Von :user nominiert.', 29 29 'nominate_modes' => 'Nominiert von :user (:modes).', 30 - 'nomination_reset' => 'Neues Problem :discussion hat die Nominierung zurückgesetzt.', 30 + 'nomination_reset' => 'Ein neues Problem :discussion hat die Nominierung zurückgesetzt (:text).', 31 31 'nomination_reset_received' => 'Nominierung von :user wurde von :source_user zurückgesetzt (:text)', 32 32 'nomination_reset_received_profile' => 'Nominierung wurde von :user zurückgesetzt (:text)', 33 33 'offset_edit' => 'Online-Offset wurde von :old zu :new geändert.', ··· 58 58 59 59 'type' => [ 60 60 'approve' => 'Approval', 61 - 'beatmap_owner_change' => 'Schwierigkeitsstufenbesitzeränderung', 61 + 'beatmap_owner_change' => 'Besitzerwechsel der Difficulty', 62 62 'discussion_delete' => 'Diskussion löschen', 63 - 'discussion_post_delete' => 'Löschen der Diskussionsantwort', 64 - 'discussion_post_restore' => 'Diskussionsantwort wiederherstellen', 63 + 'discussion_post_delete' => 'Antwort in der Diskussion entfernen', 64 + 'discussion_post_restore' => 'Antwort in der Diskussion wiederherstellen', 65 65 'discussion_restore' => 'Diskussion wiederherstellen', 66 66 'disqualify' => 'Disqualifikation', 67 67 'genre_edit' => 'Genre-Änderung', 68 - 'issue_reopen' => 'Diskussion wieder öffnen', 68 + 'issue_reopen' => 'Diskussion wiedereröffnen', 69 69 'issue_resolve' => 'Diskussion lösen', 70 70 'kudosu_allow' => 'Kudosu erlauben', 71 - 'kudosu_deny' => 'Kudosu-Verweigerung', 72 - 'kudosu_gain' => 'Kudosu-Erhalt', 71 + 'kudosu_deny' => 'Kudosu ablehnen', 72 + 'kudosu_gain' => 'Kudosu erhalten', 73 73 'kudosu_lost' => 'Kudosu-Verlust', 74 74 'kudosu_recalculate' => 'Kudosu-Neuberechnung', 75 75 'language_edit' => 'Sprachänderung', 76 76 'love' => 'Love', 77 77 'nominate' => 'Nominierung', 78 78 'nomination_reset' => 'Nominierung zurücksetzten', 79 - 'nomination_reset_received' => 'Nomination Reset erhalten', 79 + 'nomination_reset_received' => 'Nomination-Reset erhalten', 80 80 'nsfw_toggle' => 'Explizit-Markierung', 81 81 'offset_edit' => 'Offset bearbeiten', 82 82 'qualify' => 'Qualifikation', 83 83 'rank' => 'Rangliste', 84 - 'remove_from_loved' => 'Loved-Entfernung', 84 + 'remove_from_loved' => 'Loved entfernen', 85 85 ], 86 86 ];
+5 -5
resources/lang/de/beatmapset_watches.php
··· 5 5 6 6 return [ 7 7 'index' => [ 8 - 'description' => 'Dies sind deine beobachteten Diskussionen. Du wirst über neue Beiträge und Updates informiert.', 9 - 'title_compact' => 'beobachtete moddingthreads', 8 + 'description' => 'Dies sind deine gefolgten Beatmap-Diskussionen. Du wirst über neue Posts und Aktualisierungen benachrichtigt.', 9 + 'title_compact' => 'Merkliste von Beatmap-Diskussionen', 10 10 11 11 'counts' => [ 12 - 'total' => 'Beobachtete Beatmaps', 12 + 'total' => 'Gefolgte Beatmaps', 13 13 'unread' => 'Beatmaps mit neuer Aktivität', 14 14 ], 15 15 16 16 'table' => [ 17 - 'empty' => 'Du beobachtest keine Diskussionen.', 17 + 'empty' => 'Du folgst keiner Beatmap-Diskussion.', 18 18 'last_update' => 'Letzte Aktualisierung', 19 - 'open_issues' => 'Offene Vorschläge/Probleme', 19 + 'open_issues' => 'Offene Probleme', 20 20 'state' => 'Status', 21 21 'title' => 'Titel', 22 22 ],
+43 -43
resources/lang/de/beatmapsets.php
··· 24 24 ], 25 25 26 26 'index' => [ 27 - 'title' => 'Beatmaps: Liste', 27 + 'title' => 'Beatmap-Auflistung', 28 28 'guest_title' => 'Beatmaps', 29 29 ], 30 30 31 31 'panel' => [ 32 - 'empty' => 'keine beatmaps', 32 + 'empty' => 'keine Beatmaps', 33 33 34 34 'download' => [ 35 35 'all' => 'herunterladen', ··· 40 40 ], 41 41 42 42 'nominate' => [ 43 - 'hybrid_requires_modes' => 'Für ein Hybrid-Beatmapset musst du mindestens einen Spielmodus auswählen, für den du nominieren möchtest.', 44 - 'incorrect_mode' => 'Du hast keine Berechtigung, für diesen Modus zu nominieren: :mode', 45 - 'full_bn_required' => 'Du musst ein vollwertiger Nominator sein, um diese qualifizierende Nominierung durchzuführen.', 43 + 'hybrid_requires_modes' => 'Für ein Hybrid-Beatmapset musst du mindestens einen Spielmodus auswählen, den du nominieren möchtest.', 44 + 'incorrect_mode' => 'Du hast keine Berechtigung diesen Modus zu nominieren: :mode', 45 + 'full_bn_required' => 'Du musst ein vollständiger Nominator sein, um diese qualifizierende Nominierung durchzuführen.', 46 46 'too_many' => 'Nominierungsvoraussetzung bereits erfüllt.', 47 47 48 48 'dialog' => [ 49 49 'confirmation' => 'Bist du sicher, dass du diese Beatmap nominieren möchtest?', 50 50 'header' => 'Beatmap nominieren', 51 - 'hybrid_warning' => 'hinweis: du kannst nur einmal nominieren, also stelle bitte sicher, dass du für alle spielmodi nominierst, die du beabsichtigst', 52 - 'which_modes' => 'Für welche Modi nominieren?', 51 + 'hybrid_warning' => 'Hinweis: du kannst nur einmalig nominieren, also stelle bitte sicher, dass du für alle Spielmodi nominierst, die du beabsichtigst', 52 + 'which_modes' => 'Für welche Modi willst du nominieren?', 53 53 ], 54 54 ], 55 55 ··· 67 67 68 68 'details' => [ 69 69 'by_artist' => 'von :artist', 70 - 'favourite' => 'Dieses Beatmapset zu deinen Favoriten hinzufügen', 70 + 'favourite' => 'Diese Beatmap zu deinen Favoriten hinzufügen', 71 71 'favourite_login' => 'Melde dich an, um diese Beatmap zu favorisieren', 72 - 'logged-out' => 'Zum Herunterladen von Beatmaps muss man eingeloggt sein!', 72 + 'logged-out' => 'Du musst eingeloggt sein, bevor du Beatmaps herunterladen kannst!', 73 73 'mapped_by' => 'erstellt von :mapper', 74 - 'mapped_by_guest' => 'Gastschwierigkeit von :mapper', 75 - 'unfavourite' => 'Dieses Beatmapset von deinen Favoriten entfernen', 76 - 'updated_timeago' => 'zuletzt aktualisiert :timeago', 74 + 'mapped_by_guest' => 'Guest-Difficulty von :mapper', 75 + 'unfavourite' => 'Diese Beatmap von deinen Favoriten entfernen', 76 + 'updated_timeago' => 'zuletzt aktualisiert vor :timeago', 77 77 78 78 'download' => [ 79 79 '_' => 'Herunterladen', ··· 83 83 ], 84 84 85 85 'login_required' => [ 86 - 'bottom' => 'für mehr Features', 86 + 'bottom' => 'für Zugriff auf mehr Features', 87 87 'top' => 'Einloggen', 88 88 ], 89 89 ], 90 90 91 91 'details_date' => [ 92 - 'approved' => 'approved :timeago', 93 - 'loved' => 'loved :timeago', 94 - 'qualified' => 'qualifiziert :timeago', 95 - 'ranked' => 'ranked :timeago', 96 - 'submitted' => 'hochgeladen :timeago', 97 - 'updated' => 'zuletzt aktualisiert :timeago', 92 + 'approved' => 'vor :timeago approved', 93 + 'loved' => 'vor :timeago loved', 94 + 'qualified' => 'vor :timeago qualifiziert', 95 + 'ranked' => 'vor :timeago ranked', 96 + 'submitted' => 'vor :timeago hochgeladen', 97 + 'updated' => 'vor :timeago zuletzt aktualisiert', 98 98 ], 99 99 100 100 'favourites' => [ 101 - 'limit_reached' => 'Du hast zu viele favorisierte Beatmaps! Bitte entferne welche, bevor du es nochmal versuchst.', 101 + 'limit_reached' => 'Du hast zu viele Beatmaps favorisiert! Bitte entferne welche, bevor du es nochmal versuchst.', 102 102 ], 103 103 104 104 'hype' => [ 105 - 'action' => 'Wenn es dir Spaß gemacht hat, diese Map zu spielen, dann hype sie, um bei ihrem Fortschritt zum <strong>Ranked</strong>-Status zu helfen.', 105 + 'action' => 'Hat dir die Map Spaß gemacht? Hype sie, um bei ihrem Fortschritt zum <strong>Ranked</strong>-Status zu helfen.', 106 106 107 107 'current' => [ 108 108 '_' => 'Die Map ist zurzeit :status.', ··· 110 110 'status' => [ 111 111 'pending' => 'ausstehend', 112 112 'qualified' => 'qualifiziert', 113 - 'wip' => 'work-in-progress', 113 + 'wip' => 'Work-in-Progress', 114 114 ], 115 115 ], 116 116 117 117 'disqualify' => [ 118 - '_' => 'Wenn du ein Problem mit dieser Beatmap findest, disqualifiziere diese bitte :link.', 118 + '_' => 'Bitte disqualifizieren, falls du ein Problem in dieser Beatmap findest :link.', 119 119 ], 120 120 121 121 'report' => [ 122 - '_' => 'Wenn du ein Problem mit dieser Beatmap findest, melde es bitte :link, um das Team zu informieren.', 122 + '_' => 'Wenn du ein Problem in dieser Beatmap findest, melde es bitte :link, um das Team zu informieren.', 123 123 'button' => 'Problem melden', 124 124 'link' => 'hier', 125 125 ], ··· 154 154 155 155 'scoreboard' => [ 156 156 'achieved' => 'erreicht :when', 157 - 'country' => 'Landesrangliste', 157 + 'country' => 'Länder-Rangliste', 158 158 'error' => 'Die Rangliste konnte nicht geladen werden', 159 - 'friend' => 'Freundesrangliste', 159 + 'friend' => 'Freundes-Rangliste', 160 160 'global' => 'Globale Rangliste', 161 - 'supporter-link' => '<a href=":link">Hier</a> klicken, um alle tollen Features zu entdecken!', 162 - 'supporter-only' => 'Du musst Supporter sein, um Freundes- und Landesranglisten zu sehen!', 163 - 'title' => 'Ranglisten', 161 + 'supporter-link' => '<a href=":link">Hier</a> klicken, um all die tollen Features zu entdecken!', 162 + 'supporter-only' => 'Du musst osu!supporter sein, um Freundes-, Länder-, oder Mod-Ranglisten zu sehen!', 163 + 'title' => 'Punkte-Anzeige', 164 164 165 165 'headers' => [ 166 - 'accuracy' => 'Genauigkeit', 167 - 'combo' => 'Combo', 166 + 'accuracy' => 'Präzision', 167 + 'combo' => 'Maximale Combo', 168 168 'miss' => 'Miss', 169 169 'mods' => 'Mods', 170 170 'pin' => 'Anpinnen', ··· 185 185 ], 186 186 'score' => [ 187 187 'first' => 'An der Spitze', 188 - 'own' => 'Dein bester Rang', 188 + 'own' => 'Deine Bestleistung', 189 189 ], 190 190 'supporter_link' => [ 191 - '_' => 'Klicke :here um alle schönen Features zu sehen, die du bekommst!', 191 + '_' => 'Klicke :here, um all die tollen Features zu entdecken, die du bekommst!', 192 192 'here' => 'hier', 193 193 ], 194 194 ], 195 195 196 196 'stats' => [ 197 - 'cs' => 'Circle Size', 198 - 'cs-mania' => 'Tastenanzahl', 199 - 'drain' => 'HP Drain', 200 - 'accuracy' => 'Genauigkeit', 201 - 'ar' => 'Approach Rate', 202 - 'stars' => 'Star Difficulty', 203 - 'total_length' => 'Länge', 197 + 'cs' => 'Circle-Size', 198 + 'cs-mania' => 'Tasten-Anzahl', 199 + 'drain' => 'HP-Drain', 200 + 'accuracy' => 'Präzision', 201 + 'ar' => 'Approach-Rate', 202 + 'stars' => 'Star-Difficulty', 203 + 'total_length' => 'Länge (Drain length: :hit_length)', 204 204 'bpm' => 'BPM', 205 205 'count_circles' => 'Circle-Anzahl', 206 206 'count_sliders' => 'Slider-Anzahl', 207 207 'offset' => 'Online-Offset: :offset', 208 - 'user-rating' => 'Benutzerbewertungen', 209 - 'rating-spread' => 'Bewertungsverteilung', 208 + 'user-rating' => 'User-Bewertungen', 209 + 'rating-spread' => 'Bewertungs-Spread', 210 210 'nominations' => 'Nominierungen', 211 211 'playcount' => 'Playcount', 212 212 ], ··· 218 218 'qualified' => 'Qualifiziert', 219 219 'wip' => 'WIP', 220 220 'pending' => 'Ausstehend', 221 - 'graveyard' => 'Friedhof', 221 + 'graveyard' => 'Graveyard', 222 222 ], 223 223 ], 224 224
+4 -4
resources/lang/de/changelog.php
··· 20 20 21 21 'index' => [ 22 22 'page_title' => [ 23 - '_' => 'Changelog Eintrag', 23 + '_' => 'Changelog-Auflistung', 24 24 '_from' => 'Änderungen seit :from', 25 25 '_from_to' => 'Änderungen zwischen :from und :to', 26 26 '_stream' => 'Änderungen in :stream', ··· 32 32 ], 33 33 34 34 'support' => [ 35 - 'heading' => 'Dir gefällt, was du siehst?', 35 + 'heading' => 'Gefällt dir dieses Update?', 36 36 'text_1' => 'Unterstütze die weitere Entwicklung von osu! und :link!', 37 - 'text_1_link' => 'werde noch heute Supporter', 38 - 'text_2' => 'Damit treibst du nicht nur die Entwicklung schneller voran, sondern erhältst auch einige coole Features und besondere Anpassungsmöglichkeiten!', 37 + 'text_1_link' => 'werde noch heute osu!supporter', 38 + 'text_2' => 'Damit treibst du nicht nur die Entwicklung schneller voran, sondern erhältst auch einige zusätzliche Features und Anpassungsmöglichkeiten!', 39 39 ], 40 40 ];
+17 -17
resources/lang/de/chat.php
··· 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 6 return [ 7 - 'loading_users' => 'lade Benutzer...', 8 - 'talking_in' => 'sprechen in :channel', 9 - 'talking_with' => 'im gespräch mit :name', 10 - 'title_compact' => 'chat', 11 - 'unread_messages' => 'ungelesene nachrichten', 7 + 'loading_users' => 'lade User...', 8 + 'talking_in' => 'in :channel reden', 9 + 'talking_with' => 'im Gespräch mit :name', 10 + 'title_compact' => 'Chat', 11 + 'unread_messages' => 'ungelesene Nachrichten', 12 12 13 13 'cannot_send' => [ 14 - 'channel' => 'Du kannst derzeit keine Nachrichten an diesen Kanal senden. Dies kann folgende Gründe haben:', 14 + 'channel' => 'Du kannst derzeit keine Nachrichten in diesem Channel senden. Dies kann folgende Gründe haben:', 15 15 'user' => 'Du kannst derzeit keine Nachrichten an diesen User senden. Dies kann folgende Gründe haben:', 16 16 ], 17 17 18 18 'channels' => [ 19 - 'confirm_part' => 'Möchtest du diesen Kanal ausblenden? Du erhältst weiterhin Nachrichten aus diesem Kanal.', 20 - 'create' => 'ankündigung erstellen', 19 + 'confirm_part' => 'Möchtest du diesen Channel ausblenden? Du erhältst weiterhin Nachrichten aus diesem Channel.', 20 + 'create' => 'Ankündigung erstellen', 21 21 22 22 'list' => [ 23 23 'title' => [ 24 24 'ANNOUNCE' => 'Ankündigungen', 25 25 'GROUP' => 'Gruppen', 26 26 'PM' => 'Direktnachrichten', 27 - 'PUBLIC' => 'Kanäle', 27 + 'PUBLIC' => 'Channel', 28 28 ], 29 29 ], 30 30 ], ··· 35 35 ], 36 36 37 37 'labels' => [ 38 - 'description' => 'beschreibung', 39 - 'message' => 'nachricht', 40 - 'name' => 'raumname', 41 - 'users' => 'spieler zum hinzufügen', 38 + 'description' => 'Beschreibung', 39 + 'message' => 'Nachricht', 40 + 'name' => 'Raumname', 41 + 'users' => 'Spieler zum Hinzufügen', 42 42 ], 43 43 ], 44 44 45 45 'not_found' => [ 46 - 'message' => 'Hier gibt es nichts, vielleicht hast du den Kanal verlassen oder er existiert nicht...', 47 - 'title' => 'Kanal nicht gefunden', 46 + 'message' => 'Hier gibt es nichts, vielleicht hast du den Channel verlassen oder er existiert nicht...', 47 + 'title' => 'Channel nicht gefunden', 48 48 ], 49 49 50 50 'input' => [ ··· 56 56 ], 57 57 58 58 'no-conversations' => [ 59 - 'howto' => "Starte eine Unterhaltung von einem User-Profil oder einem Usercard-Popup.", 60 - 'lazer' => 'Öffentliche Kanäle, die du mit <a href=":link">osu!lazer</a> beitrittst, werden auch hier angezeigt.', 59 + 'howto' => "Starte eine Unterhaltung von einem User-Profil oder einem Usercard-Pop-up.", 60 + 'lazer' => 'Öffentliche Channel, welche du mit <a href=":link">osu!lazer</a> betrittst, werden hier auch sichtbar sein.', 61 61 'title' => 'noch keine Unterhaltungen', 62 62 ], 63 63 ];
+2 -2
resources/lang/de/client_verifications.php
··· 7 7 'completed' => [ 8 8 'home' => 'Zum Dashboard gehen', 9 9 'logout' => 'Abmelden', 10 - 'text' => 'Du kannst diesen Tab/dieses Fenster nun schließen', 10 + 'text' => 'Du kannst dieses Fenster nun schließen', 11 11 'title' => 'osu! Client-Verifizierung wurde abgeschlossen', 12 12 ], 13 13 14 14 'create' => [ 15 - 'confirm' => 'Klicke unten auf den Autorisierungs-Button, um die Client-Überprüfung abzuschließen.', 15 + 'confirm' => 'Klicke unten auf den Autorisierungs-Button, um die Client-Verifizierung abzuschließen.', 16 16 'title' => 'osu! Client-Verifizierung', 17 17 ], 18 18 ];
+11 -11
resources/lang/de/comments.php
··· 5 5 6 6 return [ 7 7 'deleted' => 'gelöscht', 8 - 'deleted_by' => 'gelöscht :timeago von :user', 9 - 'deleted_by_system' => 'system', 8 + 'deleted_by' => 'vor :timeago von :user entfernt', 9 + 'deleted_by_system' => 'System', 10 10 'deleted_count' => ':count_delimited gelöschter Kommentar|:count_delimited gelöschte Kommentare', 11 - 'edited' => 'bearbeitet :timeago von :user', 11 + 'edited' => 'vor :timeago von :user bearbeitet', 12 12 'pinned' => 'angeheftet', 13 13 'empty' => 'Noch keine Kommentare vorhanden.', 14 14 'load_replies' => 'Antworten laden', ··· 25 25 'editor' => [ 26 26 'textarea_hint' => [ 27 27 '_' => 'Drücke Enter, um zu :action. Benutze Shift+Enter für eine neue Zeile.', 28 - 'edit' => 'Speichern', 28 + 'edit' => 'speichern', 29 29 'new' => 'posten', 30 30 'reply' => 'antworten', 31 31 ], 32 32 ], 33 33 34 34 'guest_button' => [ 35 - 'new' => 'Zum Kommentieren anmelden', 36 - 'reply' => 'Zum Antworten anmelden', 35 + 'new' => 'Einloggen, um zu kommentieren', 36 + 'reply' => 'Einloggen, um zu antworten', 37 37 ], 38 38 39 39 'index' => [ 40 - 'nav_comments' => 'kommentare', 41 - 'nav_title' => 'liste', 42 - 'no_comments' => 'keine kommentare gefunden...', 40 + 'nav_comments' => 'Kommentare', 41 + 'nav_title' => 'Auflistung', 42 + 'no_comments' => 'keine Kommentare gefunden...', 43 43 ], 44 44 45 45 'placeholder' => [ 46 46 'edit' => 'Kommentar hier bearbeiten', 47 - 'new' => 'Neuen Kommentar hier eingeben', 47 + 'new' => 'Neuen Kommentar hier schreiben', 48 48 'reply' => 'Antwort hier eingeben', 49 49 ], 50 50 51 51 'show' => [ 52 - 'nav_title' => 'kommentare', 52 + 'nav_title' => 'Kommentare', 53 53 ], 54 54 ];
+25 -25
resources/lang/de/common.php
··· 4 4 // See the LICENCE file in the repository root for full licence text. 5 5 6 6 return [ 7 - 'confirmation' => 'Sicher?', 7 + 'confirmation' => 'Bist du dir sicher?', 8 8 'confirmation_unsaved' => 'Nicht gespeicherte Änderungen gehen verloren. Willst du trotzdem fortfahren?', 9 9 'saved' => 'Gespeichert', 10 10 ··· 23 23 'authorise' => 'Autorisieren', 24 24 'authorising' => 'Autorisieren...', 25 25 'back_to_previous' => 'Zur vorherigen Position zurückkehren', 26 - 'back_to_top' => 'Zum Seitenanfang', 26 + 'back_to_top' => 'Zurück zum Seitenanfang', 27 27 'cancel' => 'Abbrechen', 28 28 'change' => 'ändern', 29 29 'clear' => 'Löschen', 30 - 'click_to_copy' => 'Zum Speichern in die Zwischenablage klicken', 31 - 'click_to_copy_copied' => 'In die Zwischenablage kopiert!', 30 + 'click_to_copy' => 'klicken, um in Zwischenablage zu speichern', 31 + 'click_to_copy_copied' => 'in Zwischenablage kopiert!', 32 32 'close' => 'Schließen', 33 33 'collapse' => 'einklappen', 34 34 'delete' => 'Löschen', ··· 50 50 'search' => 'Suchen', 51 51 'see_more' => 'mehr anzeigen', 52 52 'show' => 'anzeigen', 53 - 'show_deleted' => 'Gelöschte anzeigen', 53 + 'show_deleted' => 'Zeige gelöschtes', 54 54 'show_less' => 'weniger anzeigen', 55 55 'show_more' => 'mehr anzeigen', 56 - 'show_more_options' => 'mehr optionen anzeigen', 56 + 'show_more_options' => 'mehr Optionen anzeigen', 57 57 'submit' => 'Speichern', 58 58 'unpin' => 'lösen', 59 59 'update' => 'Aktualisieren', 60 - 'upload_image' => 'bild hochladen', 60 + 'upload_image' => 'Bild hochladen', 61 61 62 62 'watch' => [ 63 - 'to_0' => 'Nicht mehr beobachten', 64 - 'to_1' => 'Beobachten', 63 + 'to_0' => 'Nicht mehr merken', 64 + 'to_1' => 'Merken', 65 65 ], 66 66 ], 67 67 68 68 'count' => [ 69 69 'badges' => ':count abzeichen|:count abzeichen', 70 - 'days' => ':count tag|:count tage', 70 + 'days' => ':count Tag|:count Tage', 71 71 'hour_short_unit' => 'std.|std.', 72 - 'hours' => ':count stunde|:count stunden', 73 - 'item' => ':count Artikel', 72 + 'hours' => ':count Stunde|:count Stunden', 73 + 'item' => ':count_delimited Artikel|:count_delimited Artikel', 74 74 'minute_short_unit' => 'min.|min.', 75 75 'minutes' => ':count Minute|:count Minuten', 76 - 'months' => ':count monat|:count monate', 77 - 'notifications' => ':count_delimited Nachricht|:count_delimited Nachrichten', 76 + 'months' => ':count Monat|:count Monate', 77 + 'notifications' => ':count_delimited Benachrichtigung|:count_delimited Benachrichtigungen', 78 78 'plus_others' => '+ :count_delimited anderer!|+ :count_delimited andere!', 79 - 'post' => ':count_delimited Beitrag|:count_delimited Beiträge', 79 + 'post' => ':count_delimited Post|:count_delimited Posts', 80 80 'second_short_unit' => 'sek.|sek.', 81 - 'star_priority' => ':count_delimited Sternpriorität|:count_delimited Sternprioritäten', 82 - 'update' => ':count_delimited Update|:count_delimited Updates', 81 + 'star_priority' => ':count_delimited Star-Priorität|:count_delimited Star-Prioritäten', 82 + 'update' => ':count_delimited Aktualisierung|:count_delimited Aktualisierungen', 83 83 'view' => ':count_delimited Aufruf|:count_delimited Aufrufe', 84 84 'years' => ':count_delimited Jahr|:count_delimited Jahre', 85 85 ], 86 86 87 87 'countdown' => [ 88 - 'days' => 'tage', 89 - 'hours' => 'stunden', 90 - 'minutes' => 'minuten', 91 - 'seconds' => 'sekunden', 88 + 'days' => 'Tage', 89 + 'hours' => 'Stunden', 90 + 'minutes' => 'Minuten', 91 + 'seconds' => 'Sekunden', 92 92 ], 93 93 94 94 'datetime' => [ ··· 109 109 ], 110 110 111 111 'dropzone' => [ 112 - 'target' => 'zum hochladen hier ablegen', 112 + 'target' => 'hier fallen lassen, um hochzuladen', 113 113 ], 114 114 115 115 'input' => [ ··· 117 117 ], 118 118 119 119 'pagination' => [ 120 - 'previous' => 'vorh.', 121 - 'next' => 'näch.', 120 + 'previous' => 'zurück', 121 + 'next' => 'weiter', 122 122 ], 123 123 124 124 'score_count' => [ ··· 153 153 ], 154 154 155 155 'title' => [ 156 - 'notice' => 'Anmerkung', 156 + 'notice' => 'Hinweis', 157 157 ], 158 158 159 159 'wrong_user' => [
+26 -26
resources/lang/de/community.php
··· 8 8 'convinced' => [ 9 9 'title' => 'Ich bin überzeugt! :D', 10 10 'support' => 'unterstütze osu!', 11 - 'gift' => 'oder verschenke \'Supporter\' an andere Spieler', 11 + 'gift' => 'oder verschenke Supporter an andere Spieler', 12 12 'instructions' => 'klick auf das Herz, um zum osu!store zu gelangen', 13 13 ], 14 14 'why-support' => [ ··· 19 19 'description' => 'Ein kleines Team entwickelt und betreibt osu!. Deine Unterstützung hilft ihnen dabei, zu, du weißt schon... leben.', 20 20 ], 21 21 'infra' => [ 22 - 'title' => 'Serverinfrastruktur', 22 + 'title' => 'Server-Infrastruktur', 23 23 'description' => 'Die Beiträge gehen an die Server, auf denen die Website, Multiplayer-Dienste, Online-Bestenlisten usw. ausgeführt werden.', 24 24 ], 25 25 'featured-artists' => [ 26 26 'title' => 'Featured Artists', 27 27 'description' => 'Mit deiner Unterstützung können wir noch mehr großartige Künstler ansprechen und somit mehr gute Musik in osu! lizenzieren!', 28 - 'link_text' => 'Sieh Dir die aktuelle Liste an &raquo;', 28 + 'link_text' => 'Sieh dir die aktuelle Liste an &raquo;', 29 29 ], 30 30 'ads' => [ 31 31 'title' => 'Halte osu! aufrecht', ··· 33 33 ], 34 34 'tournaments' => [ 35 35 'title' => 'Offizielle Turniere', 36 - 'description' => 'Hilf mit, die Durchführung (und die Preise) der offiziellen osu!-Weltcup-Turniere zu finanzieren.', 36 + 'description' => 'Hilf mit, die Durchführung (und die Preise) der offiziellen osu!-Weltmeisterschaft-Turniere zu finanzieren.', 37 37 'link_text' => 'Erkunde Turniere &raquo;', 38 38 ], 39 39 'bounty-program' => [ 40 - 'title' => 'Open-Source Prämienprogramm', 41 - 'description' => 'Unterstütze die Community-Mitarbeiter, die ihre Zeit und Mühe investiert haben, um osu! besser zu machen.', 40 + 'title' => 'Open-Source-Prämien-Programm', 41 + 'description' => 'Unterstütze Mitwirkende der Community, die ihre Zeit und Mühe investiert haben, um osu! besser zu machen.', 42 42 'link_text' => 'Finde mehr heraus &raquo;', 43 43 ], 44 44 ], 45 45 'perks' => [ 46 - 'title' => 'Oh? Was gibt\'s denn?!', 46 + 'title' => 'Cool! Was bekommt man denn?', 47 47 'osu_direct' => [ 48 48 'title' => 'osu!direct', 49 - 'description' => 'Schneller und einfacher Zugriff auf die Beatmapsuche, sogar innerhalb des Spiels.', 49 + 'description' => 'Schneller und einfacher Zugriff zum Suchen und Downloaden von Beatmaps, ohne das Spiel verlassen zu müssen.', 50 50 ], 51 51 52 52 'friend_ranking' => [ 53 - 'title' => 'Freundesrangliste', 54 - 'description' => "Finde heraus, wie Du Dich auf der Bestenliste einer Beatmap gegen Deine Freunde behauptest, sowohl im Spiel als auch auf der Webseite.", 53 + 'title' => 'Freundes-Rangliste', 54 + 'description' => "Siehe dir an wie du dich auf der Bestenliste einer Beatmap gegen deine Freunde behauptest, sowohl im Spiel als auch auf der Webseite.", 55 55 ], 56 56 57 57 'country_ranking' => [ 58 - 'title' => 'Landesrangliste', 58 + 'title' => 'Länder-Rangliste', 59 59 'description' => 'Erobere dein Land, bevor du die Welt eroberst.', 60 60 ], 61 61 ··· 66 66 67 67 'auto_downloads' => [ 68 68 'title' => 'Automatische Downloads', 69 - 'description' => 'Automatische Downloads im Multiplayer, beim Zuschauen, und wenn man Links im Chat anklickt!', 69 + 'description' => 'Beatmaps werden im Multiplayer, beim Zuschauen oder wenn man Links im Chat anklickt automatisch heruntergeladen!', 70 70 ], 71 71 72 72 'upload_more' => [ 73 73 'title' => 'Mehr Hochladen', 74 - 'description' => 'Zusätzliche "ausstehende" Beatmapplätze (pro ranked Beatmap), bis zu einem Maximum von 10.', 74 + 'description' => 'Zusätzliche ausstehende Beatmap-Slots (pro ranked Beatmap) bis zu einem Maximum von 10.', 75 75 ], 76 76 77 77 'early_access' => [ 78 78 'title' => 'Früherer Zugang', 79 - 'description' => 'Zugriff auf frühere Updates, in denen man neue Features ausprobieren kann, bevor sie veröffentlich werden!', 79 + 'description' => 'Ergattere einen frühzeitigen Zugriff auf neue Releases, in denen man neue Features ausprobieren kann, bevor sie veröffentlicht werden! Dazu gehört auch die Webseite!', 80 80 ], 81 81 82 82 'customisation' => [ 83 83 'title' => 'Anpassung', 84 - 'description' => "Füge deinem Profil eine persönliche Note hinzu, indem du eine komplett anpassbare Seite hinzufügst.", 84 + 'description' => "Füge deinem Profil eine persönliche Note hinzu, indem du ein individuelles Titelbild hochlädst oder einen vollständig anpassbaren \"Ich!\"-Bereich erstellst.", 85 85 ], 86 86 87 87 'beatmap_filters' => [ 88 - 'title' => 'Beatmapfilter', 89 - 'description' => 'Filtere Suchanfragen nach gespielten oder ungespielten Beatmaps und nach erreichtem Rang.', 88 + 'title' => 'Beatmap-Filter', 89 + 'description' => 'Filtere Beatmap-Suchanfragen nach gespielten und ungespielten Maps oder nach erreichtem Rang.', 90 90 ], 91 91 92 92 'yellow_fellow' => [ ··· 101 101 102 102 'change_username' => [ 103 103 'title' => 'Benutzernamen ändern', 104 - 'description' => 'Ändere deinen Namen ohne weitere Kosten (einmalig).', 104 + 'description' => 'Beim ersten Supporter-Kauf ist eine kostenlose Namensänderung enthalten.', 105 105 ], 106 106 107 107 'skinnables' => [ 108 108 'title' => 'Skinbare Elemente', 109 - 'description' => 'Mehr skinbare Elemente im Spiel, z. B. der Menühintergrund.', 109 + 'description' => 'Mehr skinbare Elemente im Spiel, z.B. der Menü-Hintergrund.', 110 110 ], 111 111 112 112 'feature_votes' => [ ··· 115 115 ], 116 116 117 117 'sort_options' => [ 118 - 'title' => 'Sortieroptionen', 118 + 'title' => 'Sortier-Optionen', 119 119 'description' => 'Länder-, Freundes- und modspezifische Ranglisten im Spiel einsehen!', 120 120 ], 121 121 ··· 125 125 ], 126 126 'more_friends' => [ 127 127 'title' => 'Mehr Freunde', 128 - 'description' => 'Die maximale Anzahl von Freunden, die Du haben kannst, wurde erhöht von :normally &rarr; :supporter', 128 + 'description' => 'Die maximale Anzahl von Freunden, die du haben kannst, wurde von erhöht :normally &rarr; :supporter', 129 129 ], 130 130 'more_beatmaps' => [ 131 - 'title' => 'Weitere Beatmaps hochladen', 132 - 'description' => 'Die Anzahl der non-ranked Beatmaps, die Du gleichzeitig haben kannst, wird aus einem Basiswert plus einem zusätzlichen Bonus für jede ranked Beatmap berechnet (bis zu einem Limit).<br/><br/>Normalerweise sind dies :base plus :bonus pro ranked Beatmap (bis zu :bonus_max). Mit Supporter erhöht sich dies auf :supporter_base plus :supporter_bonus pro ranked Beatmap (bis zu :supporter_bonus_max).', 131 + 'title' => 'Mehr Beatmaps hochladen', 132 + 'description' => 'Die Anzahl der unranked Beatmaps, die du gleichzeitig haben kannst, wird aus einem Basiswert plus einem zusätzlichen Bonus für jede ranked Beatmap berechnet (bis zu einem Limit).<br/><br/>Normalerweise sind dies :base plus :bonus pro ranked Beatmap (bis zu :bonus_max). Mit Supporter erhöht sich dies auf :supporter_base plus :supporter_bonus pro ranked Beatmap (bis zu :supporter_bonus_max).', 133 133 ], 134 134 'friend_filtering' => [ 135 - 'title' => 'Freundesranglisten', 136 - 'description' => 'Miss Dich mit Deinen Freunden und finde heraus, wie Du Dich gegen sie behauptest!', 135 + 'title' => 'Freundes-Ranglisten', 136 + 'description' => 'Kokurriere dich mit deinen Freunden und sieh dir an, wie du dich gegen sie behauptest!', 137 137 ], 138 138 139 139 ], 140 140 'supporter_status' => [ 141 141 'contribution' => 'Danke für deine bisherige Unterstützung! Du hast insgesamt :dollars durch :tags Supporter-Tag(s) beigesteuert!', 142 - 'gifted' => ":giftedTags deiner Supporter-Einkäufe waren Geschenke (für insgesamt :giftedDollars), wie großzügig von dir!", 142 + 'gifted' => ":giftedTags deiner Supporter-Einkäufe waren Geschenke (im Wert von :giftedDollars), wie großzügig von dir!", 143 143 'not_yet' => "Du hast noch kein osu!supporter-Tag :(", 144 144 'valid_until' => 'Dein aktuelles osu!supporter-Tag endet am :date!', 145 145 'was_valid_until' => 'Dein osu!supporter-Tag war bis :date gültig.',
+2 -2
resources/lang/de/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Foren', 29 29 'latest_post' => 'Neuester Beitrag', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Beitrag wirklich wiederherstellen?', 49 49 'edited' => 'Zuletzt von :user :when bearbeitet, insgesamt :count_delimited Mal bearbeitet.', 50 50 'posted_at' => 'erstellt :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'gepostet von :username in :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Beitrag löschen',
+29 -29
resources/lang/de/home.php
··· 6 6 return [ 7 7 'landing' => [ 8 8 'download' => 'Jetzt herunterladen', 9 - 'online' => 'aktuell <strong>:players</strong> online in <strong>:games</strong> Spielen', 9 + 'online' => 'aktuell sind <strong>:players</strong> in <strong>:games</strong> Spielen online', 10 10 'peak' => 'Maximum, :count Benutzer online', 11 11 'players' => '<strong>:count</strong> registrierte Spieler', 12 12 'title' => 'willkommen', 13 13 'see_more_news' => 'mehr Neuigkeiten anzeigen', 14 14 15 15 'slogan' => [ 16 - 'main' => 'das besteste free-to-play rhythmusspiel', 17 - 'sub' => 'der rhythmus ist nur einen klick entfernt', 16 + 'main' => 'das beste free-to-play Rhythmusspiel', 17 + 'sub' => 'Rhythmus ist nur ein Klick entfernt', 18 18 ], 19 19 ], 20 20 21 21 'search' => [ 22 22 'advanced_link' => 'Erweiterte Suche', 23 23 'button' => 'Suchen', 24 - 'empty_result' => 'Nichts gefunden!', 24 + 'empty_result' => 'Keine Treffer!', 25 25 'keyword_required' => 'Ein Suchbegriff ist erforderlich', 26 - 'placeholder' => 'Zum Suchen Text eingeben', 27 - 'title' => 'Suchergebnisse', 26 + 'placeholder' => 'tippe, um zu suchen', 27 + 'title' => 'Suche', 28 28 29 29 'beatmapset' => [ 30 - 'login_required' => 'Melde Dich an, um Beatmaps zu sehen', 30 + 'login_required' => 'Melde dich an, um Beatmaps zu suchen', 31 31 'more' => ':count weitere gefundene Beatmaps', 32 - 'more_simple' => 'Mehr gefundene Beatmaps anzeigen', 32 + 'more_simple' => 'Siehe mehr Beatmap-Ergebnisse', 33 33 'title' => 'Beatmaps', 34 34 ], 35 35 36 36 'forum_post' => [ 37 37 'all' => 'Alle Foren', 38 - 'link' => 'Das Forum durchsuchen', 39 - 'login_required' => 'Melde Dich an, um das Forum zu durchsuchen', 40 - 'more_simple' => 'Mehr gefundene Forenbeiträge anzeigen', 38 + 'link' => 'Forum durchsuchen', 39 + 'login_required' => 'Melde dich an, um das Forum zu durchsuchen', 40 + 'more_simple' => 'Siehe mehr Foren-Ergebnisse', 41 41 'title' => 'Forum', 42 42 43 43 'label' => [ 44 - 'forum' => 'in foren suchen', 45 - 'forum_children' => 'subforen einbeziehen', 44 + 'forum' => 'Foren durchsuchen', 45 + 'forum_children' => 'Subforen einbeziehen', 46 46 'include_deleted' => 'Gelöschte Beiträge miteinbeziehen', 47 47 'topic_id' => 'Thread #', 48 - 'username' => 'autor', 48 + 'username' => 'Autor', 49 49 ], 50 50 ], 51 51 52 52 'mode' => [ 53 53 'all' => 'alle', 54 - 'beatmapset' => 'beatmap', 55 - 'forum_post' => 'forum', 56 - 'user' => 'spieler', 57 - 'wiki_page' => 'wiki', 54 + 'beatmapset' => 'Beatmap', 55 + 'forum_post' => 'Forum', 56 + 'user' => 'Spieler', 57 + 'wiki_page' => 'Wiki', 58 58 ], 59 59 60 60 'user' => [ 61 61 'login_required' => 'Melde Dich an, um Benutzer zu suchen', 62 - 'more' => ':count weitere gefundene Spieler', 63 - 'more_simple' => 'Mehr gefundene Spieler anzeigen', 64 - 'more_hidden' => 'Die Spielersuche ist auf :max Spieler limitiert. Verfeinere bitte deine Suchanfrage.', 62 + 'more' => ':count weitere Spieler-Ergebnisse', 63 + 'more_simple' => 'Siehe mehr Spieler-Ergebnisse', 64 + 'more_hidden' => 'Die Spielersuche ist auf :max Spieler limitiert. Versuche deine Suchanfrage zu verfeinern.', 65 65 'title' => 'Spieler', 66 66 ], 67 67 68 68 'wiki_page' => [ 69 - 'link' => 'Das Wiki durchsuchen', 69 + 'link' => 'wiki durchsuchen', 70 70 'more_simple' => 'Mehr gefundene Wikieinträge anzeigen', 71 71 'title' => 'Wiki', 72 72 ], ··· 74 74 75 75 'download' => [ 76 76 'action' => 'osu! herunterladen', 77 - 'action_lazer' => 'Lade osu!(lazer) herunter', 78 - 'action_lazer_description' => 'die nächste Hauptversion von osu!', 77 + 'action_lazer' => 'osu!(lazer) herunterladen', 78 + 'action_lazer_description' => 'das nächste große Update von osu!', 79 79 'action_lazer_info' => 'siehe hier für weitere Informationen', 80 80 'action_lazer_title' => 'osu!(lazer) ausprobieren', 81 - 'action_title' => 'Lade osu! herunter', 81 + 'action_title' => 'osu! herunterladen', 82 82 'for_os' => 'für :os', 83 83 'lazer_note' => 'Anmerkung: Rücksetzungen der Rangliste möglich', 84 84 'macos-fallback' => 'macOS-Benutzer', ··· 107 107 'description' => 'Folge den Anweisungen beim Spielstart, um dich einzuloggen oder einen Account zu erstellen.', 108 108 ], 109 109 'download' => [ 110 - 'title' => 'Lade das Spiel herunter', 110 + 'title' => 'Spiel herunterladen', 111 111 'description' => 'Klicke zum Herunterladen oben auf den Button und führe die Installationsdatei aus!', 112 112 ], 113 113 'beatmaps' => [ 114 - 'title' => 'hol\' dir beatmaps', 114 + 'title' => 'Beatmaps runterladen', 115 115 'description' => [ 116 - '_' => ':browse durch die enorme Bibliothek an von Nutzern erstellten Beatmaps und fang an zu spielen!', 116 + '_' => ':browse durch die riesige Bibliothek von an Nutzern erstellten Beatmaps und fang an zu spielen!', 117 117 'browse' => 'Stöbere', 118 118 ], 119 119 ], ··· 121 121 ], 122 122 123 123 'user' => [ 124 - 'title' => 'dashboard', 124 + 'title' => 'Dashboard', 125 125 'news' => [ 126 126 'title' => 'News', 127 127 'error' => 'News konnten nicht geladen werden. Versuche, die Seite neu zu laden?...',
+2 -2
resources/lang/de/news.php
··· 13 13 ], 14 14 15 15 'title' => [ 16 - '_' => 'Neuigkeiten :info', 16 + '_' => 'Neuigkeiten', 17 17 'info' => 'startseite', 18 18 ], 19 19 ], ··· 27 27 ], 28 28 29 29 'title' => [ 30 - '_' => 'Neuigkeiten :info', 30 + '_' => 'Neuigkeiten', 31 31 'info' => 'Beitrag', 32 32 ], 33 33 ],
+8 -8
resources/lang/de/notifications.php
··· 6 6 return [ 7 7 'all_read' => 'Alle Benachrichtigungen gelesen!', 8 8 'delete' => ':type löschen', 9 - 'loading' => 'Ungelesene Benachrichtigungen werden geladen...', 9 + 'loading' => 'Ungelesene Benachrichtigungen werden geladen ...', 10 10 'mark_read' => ':type als gelesen markieren', 11 11 'none' => 'Keine Benachrichtigungen', 12 12 'see_all' => 'alle benachrichtigungen ansehen', ··· 14 14 'verifying' => 'Bitte verifiziere deine Sitzung, um Benachrichtigungen anzuzeigen', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'Alle', 18 + 'beatmapset' => 'Beatmaps', 19 + 'build' => 'Versionen', 20 + 'channel' => 'Chat', 21 + 'forum_topic' => 'Forum', 22 + 'news_post' => 'Neuigkeiten', 23 + 'user' => 'Profil', 24 24 ], 25 25 26 26 'filters' => [
+8 -8
resources/lang/de/page_title.php
··· 55 55 'show' => 'beatmap-info', 56 56 ], 57 57 'changelog_controller' => [ 58 - '_' => 'änderungsprotokoll', 58 + '_' => 'Änderungsprotokoll', 59 59 ], 60 60 'chat_controller' => [ 61 - '_' => 'chat', 61 + '_' => 'Chat', 62 62 ], 63 63 'comments_controller' => [ 64 64 '_' => 'kommentare', 65 65 ], 66 66 'contests_controller' => [ 67 - '_' => 'wettbewerbe', 67 + '_' => 'Wettbewerbe', 68 68 ], 69 69 'groups_controller' => [ 70 70 'show' => 'gruppen', 71 71 ], 72 72 'home_controller' => [ 73 - 'get_download' => 'herunterladen', 73 + 'get_download' => 'Herunterladen', 74 74 'index' => 'dashboard', 75 - 'search' => 'suchen', 75 + 'search' => 'Suchen', 76 76 'support_the_game' => 'Das Spiel unterstützen', 77 77 'testflight' => 'testflight', 78 78 ], ··· 86 86 '_' => 'spiele', 87 87 ], 88 88 'news_controller' => [ 89 - '_' => 'neuigkeiten', 89 + '_' => 'Neuigkeiten', 90 90 ], 91 91 'notifications_controller' => [ 92 92 '_' => 'benachrichtigungsverlauf', ··· 101 101 '_' => 'performance', 102 102 ], 103 103 'seasons_controller' => [ 104 - '_' => '', 104 + '_' => 'Ranglisten', 105 105 ], 106 106 'tournaments_controller' => [ 107 107 '_' => 'turniere', ··· 112 112 'disabled' => 'notiz', 113 113 ], 114 114 'wiki_controller' => [ 115 - '_' => 'wiki', 115 + '_' => 'Wiki', 116 116 ], 117 117 ], 118 118 'passport' => [
+5 -5
resources/lang/de/rankings.php
··· 23 23 'multiplayer' => 'mehrspieler', 24 24 'performance' => 'performance', 25 25 'score' => 'punktzahl', 26 - 'seasons' => '', 26 + 'seasons' => 'Seasons', 27 27 ], 28 28 29 29 'seasons' => [ 30 - 'empty' => '', 31 - 'ongoing' => '', 32 - 'room_count' => '', 33 - 'url' => '', 30 + 'empty' => 'Es existieren noch keine Räume in dieser Season.', 31 + 'ongoing' => 'Diese Season ist noch im Gange (es werden weitere Playlists hinzugefügt).', 32 + 'room_count' => 'Anzahl der Playlists', 33 + 'url' => 'Weitere Informationen zu dieser Season anzeigen.', 34 34 ], 35 35 36 36 'spotlight' => [
+1 -1
resources/lang/de/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Erstelldatum', 36 36 'feature_votes' => 'Sternpriorität', 37 37 'new' => 'Letzte Antwort', 38 38 ],
+1 -1
resources/lang/de/store.php
··· 72 72 'cancel_not_allowed' => 'Diese Bestellung kann zu diesem Zeitpunkt nicht storniert werden.', 73 73 'invoice' => 'Rechnung anzeigen', 74 74 'no_orders' => 'Keine Bestellungen zum anzeigen.', 75 - 'paid_on' => 'Bestellung aufgegeben am :date', 75 + 'paid_on' => 'Bestellung aufgegeben :date', 76 76 'resume' => 'Bezahlung fortsetzen', 77 77 'shopify_expired' => 'Der Zahlungslink für diese Bestellung ist abgelaufen.', 78 78
+1
resources/lang/el/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Προ-πακεταρισμένες συλλογές beatmaps βασισμένες σε ένα κοινό θέμα.', 9 + 'empty' => '', 9 10 'nav_title' => 'λίστα', 10 11 'title' => 'Πακέτα Beatmaps', 11 12
+1
resources/lang/el/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '', 202 + 'on' => '', 202 203 'queue' => '', 203 204 'soon' => 'σύντομα', 204 205 ],
+2 -2
resources/lang/en/artist.php
··· 13 13 14 14 'beatmaps' => [ 15 15 '_' => 'Beatmaps', 16 - 'download' => 'Download Beatmap Template', 17 - 'download-na' => 'Beatmap Template not yet available', 16 + 'download' => 'download beatmap template', 17 + 'download-na' => 'beatmap template not yet available', 18 18 ], 19 19 20 20 'index' => [
+1
resources/lang/en/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Pre-packaged collections of beatmaps based around a common theme.', 9 + 'empty' => 'Coming soon!', 9 10 'nav_title' => 'listing', 10 11 'title' => 'Beatmap Packs', 11 12
+2 -1
resources/lang/en/beatmaps.php
··· 106 106 'unsaved' => 'Unsaved', 107 107 'timestamp' => [ 108 108 'all-diff' => 'Posts on "All difficulties" can\'t be timestamped.', 109 - 'diff' => 'If this :type starts with a timestamp, it will be shown under Timeline.', 109 + 'diff' => 'If this post starts with a timestamp, it will be shown under Timeline.', 110 110 ], 111 111 ], 112 112 'insert-block' => [ ··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'This map is estimated to be ranked :date if no issues are found. It is #:position in the :queue.', 202 + 'on' => 'on :date', 202 203 'queue' => 'ranking queue', 203 204 'soon' => 'soon', 204 205 ],
+4 -4
resources/lang/en/beatmapsets.php
··· 67 67 68 68 'details' => [ 69 69 'by_artist' => 'by :artist', 70 - 'favourite' => 'Favourite this beatmap', 71 - 'favourite_login' => 'Sign in to favourite this beatmap', 72 - 'logged-out' => 'You need to sign in before downloading any beatmaps!', 70 + 'favourite' => 'favourite this beatmap', 71 + 'favourite_login' => 'sign in to favourite this beatmap', 72 + 'logged-out' => 'you need to sign in before downloading any beatmaps!', 73 73 'mapped_by' => 'mapped by :mapper', 74 74 'mapped_by_guest' => 'guest difficulty by :mapper', 75 - 'unfavourite' => 'Unfavourite this beatmap', 75 + 'unfavourite' => 'unfavourite this beatmap', 76 76 'updated_timeago' => 'last updated :timeago', 77 77 78 78 'download' => [
+1 -1
resources/lang/en/forum.php
··· 102 102 'preview' => 'Preview', 103 103 // TL note: this is used in the topic reply preview, when 104 104 // the user goes back from previewing to editing the reply 105 - 'preview_hide' => 'Write', 105 + 'preview_hide' => 'Edit', 106 106 'submit' => 'Post', 107 107 108 108 'necropost' => [
+1 -1
resources/lang/en/tournament.php
··· 13 13 ], 14 14 15 15 'item' => [ 16 - 'registered' => 'Registered players', 16 + 'registered' => 'registered players', 17 17 ], 18 18 19 19 'state' => [
+3 -2
resources/lang/en/users.php
··· 37 37 'blocked_count' => 'blocked users (:count)', 38 38 'hide_profile' => 'Hide profile', 39 39 'hide_comment' => 'hide', 40 + 'forum_post_text' => 'This post is hidden.', 40 41 'not_blocked' => 'That user is not blocked.', 41 42 'show_profile' => 'Show profile', 42 43 'show_comment' => 'show', ··· 415 416 'title' => 'User not found! ;_;', 416 417 ], 417 418 'page' => [ 418 - 'button' => 'Edit profile page', 419 + 'button' => 'edit profile page', 419 420 'description' => '<strong>me!</strong> is a personal customisable area in your profile page.', 420 421 'edit_big' => 'Edit me!', 421 422 'placeholder' => 'Type page content here', ··· 439 440 'stats' => [ 440 441 'hit_accuracy' => 'Hit Accuracy', 441 442 'level' => 'Level :level', 442 - 'level_progress' => 'Progress to next level', 443 + 'level_progress' => 'progress to next level', 443 444 'maximum_combo' => 'Maximum Combo', 444 445 'medals' => 'Medals', 445 446 'play_count' => 'Play Count',
+1 -1
resources/lang/es/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'La discusión de este mapa está bloqueada.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'No puedes cambiar los metadatos de un mapa nominado. Contacta con un miembro de los BN o del NAT si crees que están establecidos incorrectamente.',
+2 -1
resources/lang/es/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Colecciones preempaquetadas de mapas basadas en un tema común.', 9 + 'empty' => '', 9 10 'nav_title' => 'listado', 10 11 'title' => 'Paquetes de Mapas', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artista/Álbum', 36 37 'chart' => 'Destacados', 37 - 'featured' => '', 38 + 'featured' => 'Artista destacado', 38 39 'standard' => 'Standard', 39 40 'theme' => 'Tema', 40 41 ],
+1
resources/lang/es/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Se estima que este mapa se clasificará :date si no se encuentran problemas. Es el número :position en la :queue.', 202 + 'on' => '', 202 203 'queue' => 'cola de clasificación', 203 204 'soon' => 'pronto', 204 205 ],
+2 -2
resources/lang/es/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Foros', 29 29 'latest_post' => 'Último mensaje', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => '¿Realmente desea restaurar la publicación?', 49 49 'edited' => 'Última edición por :user :when, editado :count_delimited vez en total.|Última edición por :user :when, editado :count_delimited veces en total.', 50 50 'posted_at' => 'publicado :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'publicado por :username en :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Eliminar publicación',
+7 -7
resources/lang/es/notifications.php
··· 14 14 'verifying' => 'Verifique la sesión para ver las notificaciones', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'todas', 18 + 'beatmapset' => 'mapas', 19 + 'build' => 'versiones', 20 + 'channel' => 'chat', 21 + 'forum_topic' => 'foro', 22 + 'news_post' => 'noticias', 23 + 'user' => 'perfil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/es/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Creado', 36 36 'feature_votes' => 'Prioridad de estrella', 37 37 'new' => 'Última respuesta', 38 38 ],
+1
resources/lang/fa-IR/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'مجموعه بیتمپ های از پیش بسته بندی شده در مورد یک سبک/ژانر محبوب.', 9 + 'empty' => '', 9 10 'nav_title' => 'فهرست', 10 11 'title' => 'بسته بیت مپ ها', 11 12
+1
resources/lang/fa-IR/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '', 202 + 'on' => '', 202 203 'queue' => '', 203 204 'soon' => '', 204 205 ],
+1
resources/lang/fi/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Valmiiksi kasattuja beatmap kokoelmia, joissa yhdistyy tietty teema.', 9 + 'empty' => '', 9 10 'nav_title' => 'listaus', 10 11 'title' => 'Beatmap-Kokoelmat', 11 12
+1
resources/lang/fi/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Tämän beatmapin arvioidaan tulla hyväksytyksi :date, jos mitään ongelmia ei löydy. Se on #:position :queue.', 202 + 'on' => '', 202 203 'queue' => 'hyväksytysjonossa', 203 204 'soon' => 'pian', 204 205 ],
resources/lang/fil-PH/accounts.php resources/lang/fil/accounts.php
resources/lang/fil-PH/admin.php resources/lang/fil/admin.php
resources/lang/fil-PH/api.php resources/lang/fil/api.php
resources/lang/fil-PH/artist.php resources/lang/fil/artist.php
+1 -1
resources/lang/fil-PH/authorization.php resources/lang/fil/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Naka-lock ang beatmap na ito para sa talakayan.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Hindi maaaring baguhin ang metadata ng isang nominadong beatmap. Makipag-ugnayan sa isang miyembro ng BN o NAT kung sa palagay mo ay mali ang pagkakatakda nito.',
resources/lang/fil-PH/bbcode.php resources/lang/fil/bbcode.php
resources/lang/fil-PH/beatmap_discussion_posts.php resources/lang/fil/beatmap_discussion_posts.php
resources/lang/fil-PH/beatmap_discussions.php resources/lang/fil/beatmap_discussions.php
+1
resources/lang/fil-PH/beatmappacks.php resources/lang/fil/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Nakaimpakeng koleksyon ng mga beatmaps base sa magkatulad na tema.', 9 + 'empty' => '', 9 10 'nav_title' => 'listahan', 10 11 'title' => 'Beatmap Packs', 11 12
+1
resources/lang/fil-PH/beatmaps.php resources/lang/fil/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Ang mapa na ito ay tatantiyahin na maging ranked sa :date kapag walang isyu ang nahanap. Nasa ika-#:position ng :queue.', 202 + 'on' => '', 202 203 'queue' => 'pila ng ranking', 203 204 'soon' => 'malapit na', 204 205 ],
resources/lang/fil-PH/beatmapset_discussion_votes.php resources/lang/fil/beatmapset_discussion_votes.php
resources/lang/fil-PH/beatmapset_events.php resources/lang/fil/beatmapset_events.php
resources/lang/fil-PH/beatmapset_watches.php resources/lang/fil/beatmapset_watches.php
resources/lang/fil-PH/beatmapsets.php resources/lang/fil/beatmapsets.php
resources/lang/fil-PH/changelog.php resources/lang/fil/changelog.php
resources/lang/fil-PH/chat.php resources/lang/fil/chat.php
resources/lang/fil-PH/client_verifications.php resources/lang/fil/client_verifications.php
resources/lang/fil-PH/comments.php resources/lang/fil/comments.php
resources/lang/fil-PH/common.php resources/lang/fil/common.php
resources/lang/fil-PH/community.php resources/lang/fil/community.php
resources/lang/fil-PH/contest.php resources/lang/fil/contest.php
resources/lang/fil-PH/errors.php resources/lang/fil/errors.php
resources/lang/fil-PH/events.php resources/lang/fil/events.php
resources/lang/fil-PH/follows.php resources/lang/fil/follows.php
+2 -2
resources/lang/fil-PH/forum.php resources/lang/fil/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forums', 29 29 'latest_post' => 'Pinakabagong Post', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Talagang ibalik ang post na ito?', 49 49 'edited' => 'Huling na-edit ni pamamagitan ni :user noong :when, in-edit nang :count_delimited na beses sa kabuuan.|Huling na-edit ni pamamagitan ni :user noong :when, in-edit nang :count_delimited na beses sa kabuuan.', 50 50 'posted_at' => 'nai-post noong :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'ini-ulat ni :username sa :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Burahin ang post na ito',
resources/lang/fil-PH/friends.php resources/lang/fil/friends.php
resources/lang/fil-PH/help.php resources/lang/fil/help.php
resources/lang/fil-PH/home.php resources/lang/fil/home.php
resources/lang/fil-PH/layout.php resources/lang/fil/layout.php
resources/lang/fil-PH/livestreams.php resources/lang/fil/livestreams.php
resources/lang/fil-PH/mail.php resources/lang/fil/mail.php
resources/lang/fil-PH/matches.php resources/lang/fil/matches.php
resources/lang/fil-PH/model_validation.php resources/lang/fil/model_validation.php
resources/lang/fil-PH/model_validation/fulfillments.php resources/lang/fil/model_validation/fulfillments.php
resources/lang/fil-PH/model_validation/payments.php resources/lang/fil/model_validation/payments.php
resources/lang/fil-PH/model_validation/store/product.php resources/lang/fil/model_validation/store/product.php
resources/lang/fil-PH/multiplayer.php resources/lang/fil/multiplayer.php
resources/lang/fil-PH/news.php resources/lang/fil/news.php
+1 -1
resources/lang/fil-PH/notifications.php resources/lang/fil/notifications.php
··· 14 14 'verifying' => 'I-verify ang iyong session upang makita ang mga notipikasyon', 15 15 16 16 'action_type' => [ 17 - '_' => '', 17 + '_' => 'lahat', 18 18 'beatmapset' => '', 19 19 'build' => '', 20 20 'channel' => '',
resources/lang/fil-PH/oauth.php resources/lang/fil/oauth.php
resources/lang/fil-PH/page_title.php resources/lang/fil/page_title.php
resources/lang/fil-PH/password_reset.php resources/lang/fil/password_reset.php
resources/lang/fil-PH/paypal/errors.php resources/lang/fil/paypal/errors.php
resources/lang/fil-PH/quick_search.php resources/lang/fil/quick_search.php
resources/lang/fil-PH/rankings.php resources/lang/fil/rankings.php
resources/lang/fil-PH/report.php resources/lang/fil/report.php
resources/lang/fil-PH/scores.php resources/lang/fil/scores.php
resources/lang/fil-PH/sessions.php resources/lang/fil/sessions.php
+1 -1
resources/lang/fil-PH/sort.php resources/lang/fil/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Nilikha', 36 36 'feature_votes' => 'Prayoridad ayon sa bilang ng bituin', 37 37 'new' => 'Huling tugon', 38 38 ],
resources/lang/fil-PH/store.php resources/lang/fil/store.php
resources/lang/fil-PH/supporter_tag.php resources/lang/fil/supporter_tag.php
resources/lang/fil-PH/tournament.php resources/lang/fil/tournament.php
resources/lang/fil-PH/user_verification.php resources/lang/fil/user_verification.php
resources/lang/fil-PH/users.php resources/lang/fil/users.php
resources/lang/fil-PH/validation.php resources/lang/fil/validation.php
resources/lang/fil-PH/wiki.php resources/lang/fil/wiki.php
+1 -1
resources/lang/fr/artist.php
··· 51 51 'artist' => 'Artiste', 52 52 'bpm_gte' => 'BPM Minimum', 53 53 'bpm_lte' => 'BPM Maximum', 54 - 'empty' => 'Aucune musique correspondant aux critères de recherche n\'a été trouvé.', 54 + 'empty' => 'Aucune musique correspondant aux critères de recherche n\'a été trouvée.', 55 55 'genre' => 'Genre', 56 56 'genre_all' => 'Tous', 57 57 'length_gte' => 'Durée minimale',
+3 -3
resources/lang/fr/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'La discussion de cette beatmap est verrouillée.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Vous ne pouvez pas modifier les métadonnées d\'une beatmap nominée. Contactez un Beatmap Nominator ou un membre de la NAT si vous pensez qu\'elles sont mal définies.', ··· 64 64 'annnonce_only' => 'Ce canal est uniquement pour les annonces.', 65 65 'blocked' => 'Vous ne pouvez pas envoyer un message à un utilisateur qui vous a bloqué ou que vous avez bloqué.', 66 66 'friends_only' => 'Cet utilisateur bloque les messages des personnes qui ne sont pas dans sa liste d’amis.', 67 - 'moderated' => 'Ce salon est actuellement restreint par un modérateur.', 68 - 'no_access' => 'Vous n’avez pas accès à ce salon.', 67 + 'moderated' => 'Ce canal est actuellement restreint par un modérateur.', 68 + 'no_access' => 'Vous n’avez pas accès à ce canal.', 69 69 'receive_friends_only' => 'L\'utilisateur n\'est peut-être pas en mesure de répondre parce que vous n\'acceptez que les messages des personnes de votre liste d\'amis.', 70 70 'restricted' => 'Vous ne pouvez pas envoyer de messages en étant réduit au silence, restreint ou banni.', 71 71 'silenced' => 'Vous ne pouvez pas envoyer de messages en étant réduit au silence, restreint ou banni.',
+2 -1
resources/lang/fr/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Des collections pré-packagées de beatmaps autour d\'un thème commun.', 9 + 'empty' => '', 9 10 'nav_title' => 'liste', 10 11 'title' => 'Collections de Beatmaps', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artiste/Album', 36 37 'chart' => 'Spotlights', 37 - 'featured' => '', 38 + 'featured' => 'Featured Artist', 38 39 'standard' => 'Standard', 39 40 'theme' => 'Thème', 40 41 ],
+1
resources/lang/fr/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Cette beatmap devrait être classée le :date si aucun problème n\'est trouvé. Elle est #:position dans la :queue.', 202 + 'on' => '', 202 203 'queue' => 'file d\'attente de classement', 203 204 'soon' => 'bientôt', 204 205 ],
+2 -2
resources/lang/fr/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forums', 29 29 'latest_post' => 'Dernier message', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Voulez-vous vraiment restaurer ce post ?', 49 49 'edited' => 'Dernière édition par :user :when, modifié :count_delimited fois au total.|Dernière édition par :user :when, modifié :count_delimited fois au total.', 50 50 'posted_at' => 'posté le :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'posté par :username dans :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Supprimer le post',
+7 -7
resources/lang/fr/notifications.php
··· 14 14 'verifying' => 'Veuillez vérifier votre session pour voir les notifications', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'toutes', 18 + 'beatmapset' => 'beatmaps', 19 + 'build' => 'versions', 20 + 'channel' => 'tchat', 21 + 'forum_topic' => 'forum', 22 + 'news_post' => 'actualités', 23 + 'user' => 'profil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/fr/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Créé', 36 36 'feature_votes' => 'Star priority', 37 37 'new' => 'Dernière réponse', 38 38 ],
+1 -1
resources/lang/fr/users.php
··· 235 235 'title' => 'Beatmaps loved', 236 236 ], 237 237 'nominated' => [ 238 - 'title' => 'Beatmaps classées nommées', 238 + 'title' => 'Beatmaps classées nominées', 239 239 ], 240 240 'pending' => [ 241 241 'title' => 'Beatmaps en attente',
resources/lang/he-IL/accounts.php resources/lang/he/accounts.php
resources/lang/he-IL/admin.php resources/lang/he/admin.php
resources/lang/he-IL/api.php resources/lang/he/api.php
resources/lang/he-IL/artist.php resources/lang/he/artist.php
resources/lang/he-IL/authorization.php resources/lang/he/authorization.php
resources/lang/he-IL/bbcode.php resources/lang/he/bbcode.php
resources/lang/he-IL/beatmap_discussion_posts.php resources/lang/he/beatmap_discussion_posts.php
resources/lang/he-IL/beatmap_discussions.php resources/lang/he/beatmap_discussions.php
+1
resources/lang/he-IL/beatmappacks.php resources/lang/he/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'אוספים ארוזים מראש של beatmaps המבוססים סביב נושא משותף.', 9 + 'empty' => '', 9 10 'nav_title' => 'רשימה', 10 11 'title' => 'חבילות Beatmap', 11 12
+1
resources/lang/he-IL/beatmaps.php resources/lang/he/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'מפה זאת תקבל סטטוס Ranked ב:date במידה ולא ימצאו שגיעות. המפה נמצא ב#:position ב:queue.', 202 + 'on' => '', 202 203 'queue' => 'נבדקת לקבלת Ranking', 203 204 'soon' => 'בקרוב', 204 205 ],
resources/lang/he-IL/beatmapset_discussion_votes.php resources/lang/he/beatmapset_discussion_votes.php
resources/lang/he-IL/beatmapset_events.php resources/lang/he/beatmapset_events.php
resources/lang/he-IL/beatmapset_watches.php resources/lang/he/beatmapset_watches.php
resources/lang/he-IL/beatmapsets.php resources/lang/he/beatmapsets.php
resources/lang/he-IL/changelog.php resources/lang/he/changelog.php
resources/lang/he-IL/chat.php resources/lang/he/chat.php
resources/lang/he-IL/client_verifications.php resources/lang/he/client_verifications.php
resources/lang/he-IL/comments.php resources/lang/he/comments.php
resources/lang/he-IL/common.php resources/lang/he/common.php
resources/lang/he-IL/community.php resources/lang/he/community.php
resources/lang/he-IL/contest.php resources/lang/he/contest.php
resources/lang/he-IL/errors.php resources/lang/he/errors.php
resources/lang/he-IL/events.php resources/lang/he/events.php
resources/lang/he-IL/follows.php resources/lang/he/follows.php
resources/lang/he-IL/forum.php resources/lang/he/forum.php
resources/lang/he-IL/friends.php resources/lang/he/friends.php
resources/lang/he-IL/help.php resources/lang/he/help.php
resources/lang/he-IL/home.php resources/lang/he/home.php
resources/lang/he-IL/layout.php resources/lang/he/layout.php
resources/lang/he-IL/livestreams.php resources/lang/he/livestreams.php
resources/lang/he-IL/mail.php resources/lang/he/mail.php
resources/lang/he-IL/matches.php resources/lang/he/matches.php
resources/lang/he-IL/model_validation.php resources/lang/he/model_validation.php
resources/lang/he-IL/model_validation/fulfillments.php resources/lang/he/model_validation/fulfillments.php
resources/lang/he-IL/model_validation/payments.php resources/lang/he/model_validation/payments.php
resources/lang/he-IL/model_validation/store/product.php resources/lang/he/model_validation/store/product.php
resources/lang/he-IL/multiplayer.php resources/lang/he/multiplayer.php
resources/lang/he-IL/news.php resources/lang/he/news.php
resources/lang/he-IL/notifications.php resources/lang/he/notifications.php
resources/lang/he-IL/oauth.php resources/lang/he/oauth.php
resources/lang/he-IL/page_title.php resources/lang/he/page_title.php
resources/lang/he-IL/password_reset.php resources/lang/he/password_reset.php
resources/lang/he-IL/paypal/errors.php resources/lang/he/paypal/errors.php
resources/lang/he-IL/quick_search.php resources/lang/he/quick_search.php
resources/lang/he-IL/rankings.php resources/lang/he/rankings.php
resources/lang/he-IL/report.php resources/lang/he/report.php
resources/lang/he-IL/scores.php resources/lang/he/scores.php
resources/lang/he-IL/sessions.php resources/lang/he/sessions.php
resources/lang/he-IL/sort.php resources/lang/he/sort.php
resources/lang/he-IL/store.php resources/lang/he/store.php
resources/lang/he-IL/supporter_tag.php resources/lang/he/supporter_tag.php
resources/lang/he-IL/tournament.php resources/lang/he/tournament.php
resources/lang/he-IL/user_verification.php resources/lang/he/user_verification.php
resources/lang/he-IL/users.php resources/lang/he/users.php
resources/lang/he-IL/validation.php resources/lang/he/validation.php
resources/lang/he-IL/wiki.php resources/lang/he/wiki.php
+1
resources/lang/hr-HR/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Unaprijed zapakirane zbirke beatmapa temeljenih na zajedničkoj temi.', 9 + 'empty' => '', 9 10 'nav_title' => 'popis', 10 11 'title' => 'Paketi beatmapa', 11 12
+1
resources/lang/hr-HR/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Procjenjuje se da će ova beatmapa biti rangirana :date ako nema problema. To je :position. u :queue.', 202 + 'on' => '', 202 203 'queue' => 'red čekanja na rangiranje', 203 204 'soon' => 'uskoro', 204 205 ],
+1
resources/lang/hu/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Előre csomagolt, általános témákat körbeölelő beatmap gyűjtemények.', 9 + 'empty' => '', 9 10 'nav_title' => 'listázás', 10 11 'title' => 'Beatmap Csomagok', 11 12
+1
resources/lang/hu/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Ez a pálya rangsorolt lesz :date napján, ha további problémák nem merülnek fel. Jelenleg a :position. helyen áll a :queue.', 202 + 'on' => '', 202 203 'queue' => 'ranglistázási sorban', 203 204 'soon' => 'a közeljövő egy', 204 205 ],
+1 -1
resources/lang/id/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Diskusi pada beatmap ini telah dikunci.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Kamu tidak dapat mengubah pengaturan metadata pada beatmap yang telah dinominasikan. Harap hubungi BN atau NAT apabila kamu merasa ada suatu hal yang perlu diubah.',
+2 -1
resources/lang/id/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Paket beatmap yang tersusun atas satu tema tertentu.', 9 + 'empty' => '', 9 10 'nav_title' => 'daftar', 10 11 'title' => 'Paket Beatmap', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artis/Album', 36 37 'chart' => 'Spotlights', 37 - 'featured' => '', 38 + 'featured' => 'Featured Artist', 38 39 'standard' => 'Standar', 39 40 'theme' => 'Tematik', 40 41 ],
+1
resources/lang/id/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Map ini akan berstatus Ranked pada :date apabila tidak terdapat masalah baru yang ditemukan. Map ini berada pada urutan ke-:position dalam :queue yang ada.', 202 + 'on' => '', 202 203 'queue' => 'antrian ranking', 203 204 'soon' => 'segera', 204 205 ],
+2 -2
resources/lang/id/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forum', 29 29 'latest_post' => 'Kiriman Terbaru', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Apakah kamu yakin untuk memulihkan post ini?', 49 49 'edited' => 'Terakhir disunting oleh :user :when, dengan total penyuntingan sebanyak :count_delimited kali.|Terakhir disunting oleh :user :when, dengan total penyuntingan sebanyak :count_delimited kali.', 50 50 'posted_at' => 'diposting :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'diposting oleh :username pada :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Hapus post',
+7 -7
resources/lang/id/notifications.php
··· 14 14 'verifying' => 'Harap verifikasi sesi untuk melihat notifikasi', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'semua', 18 + 'beatmapset' => 'beatmap', 19 + 'build' => 'versi rilis', 20 + 'channel' => 'percakapan', 21 + 'forum_topic' => 'forum', 22 + 'news_post' => 'berita', 23 + 'user' => 'profil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/id/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Dibuat', 36 36 'feature_votes' => 'Prioritas', 37 37 'new' => 'Balasan Terakhir', 38 38 ],
+2 -1
resources/lang/it/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Collezioni pre-confezionate di beatmap con un tema comune.', 9 + 'empty' => '', 9 10 'nav_title' => 'lista', 10 11 'title' => 'Pacchetti Beatmap', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artista/Album', 36 37 'chart' => 'Spotlight', 37 - 'featured' => '', 38 + 'featured' => 'Artista in primo piano', 38 39 'standard' => 'Standard', 39 40 'theme' => 'Tema', 40 41 ],
+2 -1
resources/lang/it/beatmaps.php
··· 141 141 'approved' => 'Questa beatmap è stata approvata il :date!', 142 142 'graveyard' => "Questa beatmap non è stata aggiornata dal :date ed è stata molto probabilmente abbandonata...", 143 143 'loved' => 'Questa beatmap è stata aggiunta a quelle amate il :date!', 144 - 'ranked' => 'Questa beatmap è stata classificata il :date!', 144 + 'ranked' => 'Questa beatmap è stata classificata il giorno :date!', 145 145 'wip' => 'Nota: Questa beatmap è contrassegnata come work-in-progress dal creatore.', 146 146 ], 147 147 ··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'È stimato che questa mappa verrà classificata in data :date se non vengono trovati problemi. È in posizione #:position nella :queue.', 202 + 'on' => '', 202 203 'queue' => 'coda per la classifica', 203 204 'soon' => 'molto vicina', 204 205 ],
+1 -1
resources/lang/it/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forum', 29 29 'latest_post' => 'Post più recente', 30 30 31 31 'index' => [
+7 -7
resources/lang/it/notifications.php
··· 14 14 'verifying' => 'Verifica la sessione per visualizzare le notifiche', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => '"tutto"', 18 + 'beatmapset' => '"beatmap"', 19 + 'build' => '"versioni"', 20 + 'channel' => '"chat"', 21 + 'forum_topic' => '"forum"', 22 + 'news_post' => '"notizie"', 23 + 'user' => '"profilo"', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/it/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Creazione', 36 36 'feature_votes' => 'Stelle di priorità', 37 37 'new' => 'Ultima risposta', 38 38 ],
+1 -1
resources/lang/it/store.php
··· 9 9 'info' => ':count_delimited articolo nel carrello ($:subtotal)|:count_delimited articoli nel carrello ($:subtotal)', 10 10 'more_goodies' => 'Voglio dare un\'occhiata ad altri elementi prima di completare l\'ordine', 11 11 'shipping_fees' => 'costi di spedizione', 12 - 'title' => 'Carrello della spesa', 12 + 'title' => 'Carrello', 13 13 'total' => 'totale', 14 14 15 15 'errors_no_checkout' => [
+2 -1
resources/lang/ja/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '共通のテーマを有するビートマップを集めたパックです。', 9 + 'empty' => '', 9 10 'nav_title' => '一覧', 10 11 'title' => 'ビートマップパック', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'アーティスト/アルバム', 36 37 'chart' => 'スポットライト', 37 - 'featured' => '', 38 + 'featured' => '注目アーティスト', 38 39 'standard' => 'スタンダードパック', 39 40 'theme' => 'テーマ', 40 41 ],
+1
resources/lang/ja/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'このマップは、問題が見つからなければ :date にrankedされると推定されます。:queue 内の #:position ', 202 + 'on' => '', 202 203 'queue' => 'ランキングキュー', 203 204 'soon' => '間もなく', 204 205 ],
+1 -1
resources/lang/ja/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'フォーラム', 29 29 'latest_post' => '最新の投稿', 30 30 31 31 'index' => [
+1 -1
resources/lang/ko/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => '이 비트맵은 토론을 할 수 없도록 잠겨 있습니다.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => '추천된 맵의 메타데이터를 변경할 수는 없습니다. 잘못 지정된 것 같으시면 BN이나 NAT 멤버에게 알려주세요.',
+2 -1
resources/lang/ko/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '비슷한 테마를 기준으로 모아놓은 비트맵 모음집입니다.', 9 + 'empty' => '', 9 10 'nav_title' => '목록', 10 11 'title' => '비트맵 팩', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => '아티스트/앨범', 36 37 'chart' => '스포트라이트', 37 - 'featured' => '', 38 + 'featured' => '공식 아티스트', 38 39 'standard' => '표준', 39 40 'theme' => '테마', 40 41 ],
+1
resources/lang/ko/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '이 맵에 아무런 문제가 발견되지 않으면 :date 에 랭크될 예정입니다. :queue의 #:position번째 순서입니다.', 202 + 'on' => '', 202 203 'queue' => '랭킹 대기열', 203 204 'soon' => '곧', 204 205 ],
+2 -2
resources/lang/ko/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => '포럼', 29 29 'latest_post' => '최근 게시글', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => '정말 이 글을 복원할까요?', 49 49 'edited' => ':user 님이 마지막으로 :when에 수정하여 총 :count_delimited회 수정되었습니다.', 50 50 'posted_at' => ':when에 게시됨', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => ':username 님이 :forum에 게시함', 52 52 53 53 'actions' => [ 54 54 'destroy' => '삭제',
+7 -7
resources/lang/ko/notifications.php
··· 14 14 'verifying' => '알림을 보려면 세션을 검증해주세요.', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => '모두', 18 + 'beatmapset' => '비트맵', 19 + 'build' => '빌드', 20 + 'channel' => '채팅', 21 + 'forum_topic' => '포럼', 22 + 'news_post' => '소식', 23 + 'user' => '프로필', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/ko/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => '최근 생성', 36 36 'feature_votes' => '별 평점 순위', 37 37 'new' => '최근 응답', 38 38 ],
resources/lang/lt-LT/accounts.php resources/lang/lt/accounts.php
resources/lang/lt-LT/admin.php resources/lang/lt/admin.php
resources/lang/lt-LT/api.php resources/lang/lt/api.php
resources/lang/lt-LT/artist.php resources/lang/lt/artist.php
resources/lang/lt-LT/authorization.php resources/lang/lt/authorization.php
resources/lang/lt-LT/bbcode.php resources/lang/lt/bbcode.php
resources/lang/lt-LT/beatmap_discussion_posts.php resources/lang/lt/beatmap_discussion_posts.php
resources/lang/lt-LT/beatmap_discussions.php resources/lang/lt/beatmap_discussions.php
+1
resources/lang/lt-LT/beatmappacks.php resources/lang/lt/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Surinktos bitmapų kolekcijos pagal temas.', 9 + 'empty' => '', 9 10 'nav_title' => 'sąrašas', 10 11 'title' => 'Bitmapų Rinkiniai', 11 12
+1
resources/lang/lt-LT/beatmaps.php resources/lang/lt/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Numatoma, kad šis bitmapas bus reitinguotas :date, jei nebus rasta problemų. Jis yra #:position tarp :queue.', 202 + 'on' => '', 202 203 'queue' => 'reitingavimo eilės', 203 204 'soon' => 'greitai', 204 205 ],
resources/lang/lt-LT/beatmapset_discussion_votes.php resources/lang/lt/beatmapset_discussion_votes.php
resources/lang/lt-LT/beatmapset_events.php resources/lang/lt/beatmapset_events.php
resources/lang/lt-LT/beatmapset_watches.php resources/lang/lt/beatmapset_watches.php
resources/lang/lt-LT/beatmapsets.php resources/lang/lt/beatmapsets.php
resources/lang/lt-LT/changelog.php resources/lang/lt/changelog.php
resources/lang/lt-LT/chat.php resources/lang/lt/chat.php
resources/lang/lt-LT/client_verifications.php resources/lang/lt/client_verifications.php
resources/lang/lt-LT/comments.php resources/lang/lt/comments.php
resources/lang/lt-LT/common.php resources/lang/lt/common.php
resources/lang/lt-LT/community.php resources/lang/lt/community.php
resources/lang/lt-LT/contest.php resources/lang/lt/contest.php
resources/lang/lt-LT/errors.php resources/lang/lt/errors.php
resources/lang/lt-LT/events.php resources/lang/lt/events.php
resources/lang/lt-LT/follows.php resources/lang/lt/follows.php
resources/lang/lt-LT/forum.php resources/lang/lt/forum.php
resources/lang/lt-LT/friends.php resources/lang/lt/friends.php
resources/lang/lt-LT/help.php resources/lang/lt/help.php
resources/lang/lt-LT/home.php resources/lang/lt/home.php
resources/lang/lt-LT/layout.php resources/lang/lt/layout.php
resources/lang/lt-LT/livestreams.php resources/lang/lt/livestreams.php
resources/lang/lt-LT/mail.php resources/lang/lt/mail.php
resources/lang/lt-LT/matches.php resources/lang/lt/matches.php
resources/lang/lt-LT/model_validation.php resources/lang/lt/model_validation.php
resources/lang/lt-LT/model_validation/fulfillments.php resources/lang/lt/model_validation/fulfillments.php
resources/lang/lt-LT/model_validation/payments.php resources/lang/lt/model_validation/payments.php
resources/lang/lt-LT/model_validation/store/product.php resources/lang/lt/model_validation/store/product.php
resources/lang/lt-LT/multiplayer.php resources/lang/lt/multiplayer.php
resources/lang/lt-LT/news.php resources/lang/lt/news.php
resources/lang/lt-LT/notifications.php resources/lang/lt/notifications.php
resources/lang/lt-LT/oauth.php resources/lang/lt/oauth.php
resources/lang/lt-LT/page_title.php resources/lang/lt/page_title.php
resources/lang/lt-LT/password_reset.php resources/lang/lt/password_reset.php
resources/lang/lt-LT/paypal/errors.php resources/lang/lt/paypal/errors.php
resources/lang/lt-LT/quick_search.php resources/lang/lt/quick_search.php
resources/lang/lt-LT/rankings.php resources/lang/lt/rankings.php
resources/lang/lt-LT/report.php resources/lang/lt/report.php
resources/lang/lt-LT/scores.php resources/lang/lt/scores.php
resources/lang/lt-LT/sessions.php resources/lang/lt/sessions.php
resources/lang/lt-LT/sort.php resources/lang/lt/sort.php
resources/lang/lt-LT/store.php resources/lang/lt/store.php
resources/lang/lt-LT/supporter_tag.php resources/lang/lt/supporter_tag.php
resources/lang/lt-LT/tournament.php resources/lang/lt/tournament.php
resources/lang/lt-LT/user_verification.php resources/lang/lt/user_verification.php
resources/lang/lt-LT/users.php resources/lang/lt/users.php
resources/lang/lt-LT/validation.php resources/lang/lt/validation.php
resources/lang/lt-LT/wiki.php resources/lang/lt/wiki.php
+1
resources/lang/lv-LV/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Iepriekš sagatavotas bītmapju kolekcijas ar savstarpēji kopīgu tēmu.', 9 + 'empty' => '', 9 10 'nav_title' => 'saraksts', 10 11 'title' => 'Bītmapju Pakas', 11 12
+1
resources/lang/lv-LV/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Tiek paredzēts, ka šī mape tiks ierindota :date, ja netiks konstatētas problēmas. Tā ir #:position :queue.', 202 + 'on' => '', 202 203 'queue' => 'ierindošanas rinda', 203 204 'soon' => 'drīz', 204 205 ],
+1
resources/lang/ms-MY/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '', 9 + 'empty' => '', 9 10 'nav_title' => '', 10 11 'title' => '', 11 12
+1
resources/lang/ms-MY/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '', 202 + 'on' => '', 202 203 'queue' => '', 203 204 'soon' => '', 204 205 ],
+1 -1
resources/lang/nl/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Deze beatmap is gesloten voor discussie.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'U kunt metagegevens van een nominale kaart niet wijzigen. Neem contact op met een BN of NAT lid als u denkt dat deze onjuist is ingesteld.',
+2 -1
resources/lang/nl/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Voorverpakte collecties van beatmaps rond een bepaald thema.', 9 + 'empty' => '', 9 10 'nav_title' => 'lijst', 10 11 'title' => 'Beatmap Packs', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artiest/Album', 36 37 'chart' => 'In de schijnwerpers', 37 - 'featured' => '', 38 + 'featured' => 'Uitgelichte Artiest', 38 39 'standard' => 'Standaard', 39 40 'theme' => 'Thema', 40 41 ],
+1
resources/lang/nl/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Deze map staat gepland om ranked te worden op :date als er geen problemen worden gevonden. Het is #:position in de :queue.', 202 + 'on' => '', 202 203 'queue' => 'ranking wachtlijst', 203 204 'soon' => 'binnenkort', 204 205 ],
+2 -2
resources/lang/nl/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forums', 29 29 'latest_post' => 'Laatste bericht', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Will je deze post echt terugzetten?', 49 49 'edited' => 'Laatst bewerkt door :user op :when. :count keer bewerkt in totaal.', 50 50 'posted_at' => 'gepost op :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'gepost door :username in :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Verwijder bericht',
+7 -7
resources/lang/nl/notifications.php
··· 14 14 'verifying' => 'Verifieer de sessie om meldingen te bekijken', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'alle', 18 + 'beatmapset' => 'beatmaps', 19 + 'build' => 'versies', 20 + 'channel' => 'chat', 21 + 'forum_topic' => 'forum', 22 + 'news_post' => 'nieuws', 23 + 'user' => 'profiel', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/nl/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Aangemaakt', 36 36 'feature_votes' => 'Ster prioriteit', 37 37 'new' => 'Laatste antwoord', 38 38 ],
+1
resources/lang/no/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Forhåndspakkede samlinger av beatmaps basert rundt et felles tema.', 9 + 'empty' => '', 9 10 'nav_title' => 'liste', 10 11 'title' => 'Beatmappakker', 11 12
+1
resources/lang/no/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Dette kartet er estimert til å bli rangert som :date hvis ingen problemer er funnet. Det er #:position i :queue.', 202 + 'on' => '', 202 203 'queue' => 'rangerings kø ', 203 204 'soon' => 'snart', 204 205 ],
+1 -1
resources/lang/pl/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Tworzenie dyskusji dla tej beatmapy zostało zablokowane.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Nie możesz zmienić metadanych nominowanej mapy. Skontaktuj się z członkiem BN lub NAT, jeśli uważasz, że są one ustawione nieprawidłowo.',
+2 -1
resources/lang/pl/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Kolekcje beatmap o wspólnej tematyce.', 9 + 'empty' => '', 9 10 'nav_title' => 'lista', 10 11 'title' => 'Paczki beatmap', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artysta/Album', 36 37 'chart' => 'Wyróżnione', 37 - 'featured' => '', 38 + 'featured' => 'Wyróżnieni artyści', 38 39 'standard' => 'Standardowe', 39 40 'theme' => 'Tematyczne', 40 41 ],
+1
resources/lang/pl/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Ta beatmapa uzyska status rankingowy :date, jeżeli nie zostaną zgłoszone żadne problemy. Obecnie jest ona na :position. miejscu w :queue.', 202 + 'on' => '', 202 203 'queue' => 'kolejce rankingowej', 203 204 'soon' => 'wkrótce', 204 205 ],
+1 -1
resources/lang/pl/beatmapsets.php
··· 68 68 'details' => [ 69 69 'by_artist' => ':artist', 70 70 'favourite' => 'Dodaj do ulubionych', 71 - 'favourite_login' => 'Zaloguj się, by dodać tę beatmapę do ulubionych', 71 + 'favourite_login' => 'Zaloguj się, by dodać tę beatmapę do ulubionych.', 72 72 'logged-out' => 'Zaloguj się, aby zacząć pobierać beatmapy!', 73 73 'mapped_by' => 'autorstwa :mapper', 74 74 'mapped_by_guest' => 'gościnny poziom trudności autorstwa :mapper',
+2 -2
resources/lang/pl/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forum', 29 29 'latest_post' => 'Ostatni post', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Czy na pewno chcesz przywrócić post?', 49 49 'edited' => 'Ostatnio edytowane przez :user :when, łącznie edytowane :count_delimited raz.|Ostatnio edytowane przez :user :when, łącznie edytowane :count_delimited razy.|Ostatnio edytowane przez :user :when, łącznie edytowane :count_delimited razy.', 50 50 'posted_at' => 'opublikowane :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'opublikowane przez :username na :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Usuń post',
+7 -7
resources/lang/pl/notifications.php
··· 14 14 'verifying' => 'Zweryfikuj sesję, by wyświetlić powiadomienia', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'wszystkie', 18 + 'beatmapset' => 'beatmapy', 19 + 'build' => 'zmiany', 20 + 'channel' => 'czat', 21 + 'forum_topic' => 'forum', 22 + 'news_post' => 'aktualności', 23 + 'user' => 'profil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/pl/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Data utworzenia', 36 36 'feature_votes' => 'Priorytet', 37 37 'new' => 'Ostatnia odpowiedź', 38 38 ],
+1 -1
resources/lang/pt-br/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Este beatmap está trancado para discussão.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Você não pode alterar os metadados de um mapa nomeado. Entre em contato com um membro do BN ou NAT se você acha que ele está definido incorretamente.',
+2 -1
resources/lang/pt-br/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Coleções temáticas pré-compactadas.', 9 + 'empty' => '', 9 10 'nav_title' => 'listagem', 10 11 'title' => 'Pacotes de Beatmaps', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artista/Álbum', 36 37 'chart' => 'Destaques', 37 - 'featured' => '', 38 + 'featured' => 'Artista em Destaque', 38 39 'standard' => 'Standard', 39 40 'theme' => 'Tema', 40 41 ],
+1
resources/lang/pt-br/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Este mapa é estimado a ser ranqueado em :date se nenhum problema for encontrado. É o #:position na :queue.', 202 + 'on' => '', 202 203 'queue' => 'fila de ranqueamento', 203 204 'soon' => 'em breve', 204 205 ],
+2 -2
resources/lang/pt-br/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Fóruns', 29 29 'latest_post' => 'Última Publicação', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Deseja mesmo restaurar a publicação?', 49 49 'edited' => 'Última edição por :user :when, editado :count vezes no total.', 50 50 'posted_at' => 'publicado :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'postado por :username em :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Excluir publicação',
+7 -7
resources/lang/pt-br/notifications.php
··· 14 14 'verifying' => 'Por favor verifique a sessão para ver as notificações', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'todas', 18 + 'beatmapset' => 'beatmaps', 19 + 'build' => 'versões', 20 + 'channel' => 'chat', 21 + 'forum_topic' => 'fórum', 22 + 'news_post' => 'notícias', 23 + 'user' => 'perfil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/pt-br/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Criado', 36 36 'feature_votes' => 'Prioridade de estrela', 37 37 'new' => 'Última resposta', 38 38 ],
+2 -1
resources/lang/pt/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Coleções pré-empacotadas de beatmaps baseadas num tema em comum.', 9 + 'empty' => '', 9 10 'nav_title' => 'listagem', 10 11 'title' => 'Pacotes de beatmap', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artista/Álbum', 36 37 'chart' => 'Em destaque', 37 - 'featured' => '', 38 + 'featured' => 'Artista Destacado', 38 39 'standard' => 'Padrão', 39 40 'theme' => 'Tema', 40 41 ],
+1
resources/lang/pt/beatmaps.php
··· 200 200 201 201 'rank_estimate' => [ 202 202 '_' => 'Este mapa está estimado a ser classificado em :date se não forem descobertos quaisquer problemas. Está em #:position na :queue.', 203 + 'on' => '', 203 204 'queue' => 'fila de classificação', 204 205 'soon' => 'em breve', 205 206 ],
+2 -2
resources/lang/pt/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Fóruns', 29 29 'latest_post' => 'Última publicação', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Queres mesmo restaurar a publicação?', 49 49 'edited' => 'Editado pela última vez por :user :when, editado :count vezes no total.', 50 50 'posted_at' => 'publicado :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'publicado por :username em :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Eliminar publicação',
+4 -4
resources/lang/pt/notifications.php
··· 15 15 16 16 'action_type' => [ 17 17 '_' => '', 18 - 'beatmapset' => '', 18 + 'beatmapset' => 'beatmaps', 19 19 'build' => '', 20 20 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 21 + 'forum_topic' => 'fórum', 22 + 'news_post' => 'notícias', 23 + 'user' => 'perfil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/ro/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Discuțiile sunt blocate pe acest beatmap.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Nu poți schimba datele melodiei a unui beatmap nominalizat. Contactează un membru BN sau NAT dacă crezi că e setat greșit.',
+2 -1
resources/lang/ro/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Colecțiile preambalate de beatmap-uri sunt bazate în jurul unei teme comune.', 9 + 'empty' => '', 9 10 'nav_title' => 'listare', 10 11 'title' => 'Pachete Beatmap', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Artist/Album', 36 37 'chart' => 'Promovări', 37 - 'featured' => '', 38 + 'featured' => 'Artist Oficial', 38 39 'standard' => 'Standard', 39 40 'theme' => 'Temă', 40 41 ],
+1
resources/lang/ro/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Acest beatmap este estimat a fi clasificat în data de :date dacă nu sunt găsite probleme. Este #:position în :queue.', 202 + 'on' => '', 202 203 'queue' => 'lista de așteptare pentru clasament', 203 204 'soon' => 'în curând', 204 205 ],
+2 -2
resources/lang/ro/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forum-uri', 29 29 'latest_post' => 'Ultima Postare', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Sigur dorești să restaurezi postarea?', 49 49 'edited' => 'Editat ultima dată de către :user :when, editat o dată în total.|Editat ultima dată de către :user :when, editat de :count_delimited ori în total.', 50 50 'posted_at' => 'postat :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'postat de :username în :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Șterge postarea',
+7 -7
resources/lang/ro/notifications.php
··· 14 14 'verifying' => 'Verificați sesiunea pentru a vizualiza notificările', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'tot', 18 + 'beatmapset' => 'beatmap-uri', 19 + 'build' => 'versiuni', 20 + 'channel' => 'chat', 21 + 'forum_topic' => 'forum', 22 + 'news_post' => 'știri', 23 + 'user' => 'profil', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/ro/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Creat', 36 36 'feature_votes' => 'Prioritatea stelelor', 37 37 'new' => 'Ultimul răspuns', 38 38 ],
+1 -1
resources/lang/ru/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Обсуждение этой карты закрыто.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Вы не можете изменить метаданные номинируемой карты. В случае ошибок свяжитесь с номнатором или членом NAT.',
+3 -2
resources/lang/ru/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Предварительно упакованные коллекции карт, основанные на общей тематике.', 9 + 'empty' => '', 9 10 'nav_title' => 'список', 10 11 'title' => 'Сборки карт', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Исполнители/Альбомы', 36 37 'chart' => 'Чарты', 37 - 'featured' => '', 38 + 'featured' => 'Избранные исполнители', 38 39 'standard' => 'Стандартные', 39 - 'theme' => 'Темы', 40 + 'theme' => 'Тематические', 40 41 ], 41 42 42 43 'require_login' => [
+1
resources/lang/ru/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Эта карта станет рейтинговой :date, если не будет найдено проблем. Она #:position в :queue.', 202 + 'on' => '', 202 203 'queue' => 'очереди ранка', 203 204 'soon' => 'скоро', 204 205 ],
+2 -2
resources/lang/ru/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Форумы', 29 29 'latest_post' => 'Последний пост', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Восстановить ответ?', 49 49 'edited' => 'Последний раз редактировал :user :when, всего правок: :count_delimited.', 50 50 'posted_at' => 'создана :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'опубликовано :username в :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Удалить ответ',
+7 -7
resources/lang/ru/notifications.php
··· 14 14 'verifying' => 'Пожалуйста, проверьте сессию для просмотра уведомлений', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'все', 18 + 'beatmapset' => 'карты', 19 + 'build' => 'билды', 20 + 'channel' => 'чат', 21 + 'forum_topic' => 'форум', 22 + 'news_post' => 'новости', 23 + 'user' => 'профиль', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/ru/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Дате создания', 36 36 'feature_votes' => 'Количество голосов', 37 37 'new' => 'Последнему ответу', 38 38 ],
+1 -1
resources/lang/ru/store.php
··· 119 119 'add_to_cart' => 'Добавить в корзину', 120 120 'notify' => 'Сообщить о поступлении!', 121 121 122 - 'notification_success' => 'Вы будете оповещены когда товар будет в наличии. Нажмите :link для отмены', 122 + 'notification_success' => 'Вы будете оповещены, когда товар будет в наличии. Нажмите :link для отмены', 123 123 'notification_remove_text' => 'сюда', 124 124 125 125 'notification_in_stock' => 'Данный товар уже есть в наличии!',
+1
resources/lang/si-LK/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '', 9 + 'empty' => '', 9 10 'nav_title' => '', 10 11 'title' => '', 11 12
+1
resources/lang/si-LK/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '', 202 + 'on' => '', 202 203 'queue' => '', 203 204 'soon' => '', 204 205 ],
+1
resources/lang/sk/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Kolekcie beatmáp s podobnou tématikou.', 9 + 'empty' => '', 9 10 'nav_title' => 'výpis', 10 11 'title' => 'Balíky beatmáp', 11 12
+1
resources/lang/sk/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Táto mapa je predpokladaná k hodnotení :date, pokud sa nenájdu žiadne chyby. Mapa je momentálne v pozícii číslo :position ve fronte :queue.', 202 + 'on' => '', 202 203 'queue' => 'fronta hodnotení ', 203 204 'soon' => 'neskôr ', 204 205 ],
resources/lang/sl-SI/accounts.php resources/lang/sl/accounts.php
resources/lang/sl-SI/admin.php resources/lang/sl/admin.php
resources/lang/sl-SI/api.php resources/lang/sl/api.php
resources/lang/sl-SI/artist.php resources/lang/sl/artist.php
+1 -1
resources/lang/sl-SI/authorization.php resources/lang/sl/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Ta beatmapa je zaklenjena za razpravo.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Urejanje metadata nominirane beatmape ni mogoče. Kontaktiraj člana BN ali NAT če misliš, da je ta narobe nastavljen.',
resources/lang/sl-SI/bbcode.php resources/lang/sl/bbcode.php
resources/lang/sl-SI/beatmap_discussion_posts.php resources/lang/sl/beatmap_discussion_posts.php
resources/lang/sl-SI/beatmap_discussions.php resources/lang/sl/beatmap_discussions.php
+1
resources/lang/sl-SI/beatmappacks.php resources/lang/sl/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Zapakirane zbirke beatmapov na skupno temo.', 9 + 'empty' => '', 9 10 'nav_title' => 'seznam', 10 11 'title' => 'Paketi beatmapov', 11 12
+1
resources/lang/sl-SI/beatmaps.php resources/lang/sl/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Ta beatmapa bo približno rankirana :date če ne bo najdenih težav. Trenutno je #:position v :queue.', 202 + 'on' => '', 202 203 'queue' => 'čakalna vrsta rankiranja', 203 204 'soon' => 'kmalu', 204 205 ],
resources/lang/sl-SI/beatmapset_discussion_votes.php resources/lang/sl/beatmapset_discussion_votes.php
resources/lang/sl-SI/beatmapset_events.php resources/lang/sl/beatmapset_events.php
resources/lang/sl-SI/beatmapset_watches.php resources/lang/sl/beatmapset_watches.php
+2 -2
resources/lang/sl-SI/beatmapsets.php resources/lang/sl/beatmapsets.php
··· 61 61 'discussion' => 'Razprava', 62 62 63 63 'deleted_banner' => [ 64 - 'title' => '', 65 - 'message' => '', 64 + 'title' => 'Ta beatmapa je bila izbrisana.', 65 + 'message' => '(to lahko vidijo samo moderatorji)', 66 66 ], 67 67 68 68 'details' => [
resources/lang/sl-SI/changelog.php resources/lang/sl/changelog.php
resources/lang/sl-SI/chat.php resources/lang/sl/chat.php
resources/lang/sl-SI/client_verifications.php resources/lang/sl/client_verifications.php
resources/lang/sl-SI/comments.php resources/lang/sl/comments.php
resources/lang/sl-SI/common.php resources/lang/sl/common.php
resources/lang/sl-SI/community.php resources/lang/sl/community.php
resources/lang/sl-SI/contest.php resources/lang/sl/contest.php
resources/lang/sl-SI/errors.php resources/lang/sl/errors.php
resources/lang/sl-SI/events.php resources/lang/sl/events.php
resources/lang/sl-SI/follows.php resources/lang/sl/follows.php
+2 -2
resources/lang/sl-SI/forum.php resources/lang/sl/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Forumi', 29 29 'latest_post' => 'Zadnja objava', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Res želiš povrniti objavo?', 49 49 'edited' => 'Nazadnje uredil :user :when, uredil skupno :count_delimited-krat.|Nazadnje uredil :user :when, uredil skupno :count_delimited-krat.', 50 50 'posted_at' => 'objavljeno :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'objavil :username v :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Odstrani objavo',
resources/lang/sl-SI/friends.php resources/lang/sl/friends.php
resources/lang/sl-SI/help.php resources/lang/sl/help.php
resources/lang/sl-SI/home.php resources/lang/sl/home.php
resources/lang/sl-SI/layout.php resources/lang/sl/layout.php
resources/lang/sl-SI/livestreams.php resources/lang/sl/livestreams.php
resources/lang/sl-SI/mail.php resources/lang/sl/mail.php
resources/lang/sl-SI/matches.php resources/lang/sl/matches.php
resources/lang/sl-SI/model_validation.php resources/lang/sl/model_validation.php
resources/lang/sl-SI/model_validation/fulfillments.php resources/lang/sl/model_validation/fulfillments.php
resources/lang/sl-SI/model_validation/payments.php resources/lang/sl/model_validation/payments.php
resources/lang/sl-SI/model_validation/store/product.php resources/lang/sl/model_validation/store/product.php
resources/lang/sl-SI/multiplayer.php resources/lang/sl/multiplayer.php
resources/lang/sl-SI/news.php resources/lang/sl/news.php
+5 -5
resources/lang/sl-SI/notifications.php resources/lang/sl/notifications.php
··· 14 14 'verifying' => 'Za ogled obvestil te prosimo za verifikacijo seje', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 17 + '_' => 'vse', 18 + 'beatmapset' => 'beatmape', 19 19 'build' => '', 20 20 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 21 + 'forum_topic' => 'forum', 22 + 'news_post' => 'novice', 23 + 'user' => 'profil', 24 24 ], 25 25 26 26 'filters' => [
resources/lang/sl-SI/oauth.php resources/lang/sl/oauth.php
resources/lang/sl-SI/page_title.php resources/lang/sl/page_title.php
resources/lang/sl-SI/password_reset.php resources/lang/sl/password_reset.php
resources/lang/sl-SI/paypal/errors.php resources/lang/sl/paypal/errors.php
resources/lang/sl-SI/quick_search.php resources/lang/sl/quick_search.php
resources/lang/sl-SI/rankings.php resources/lang/sl/rankings.php
resources/lang/sl-SI/report.php resources/lang/sl/report.php
resources/lang/sl-SI/scores.php resources/lang/sl/scores.php
resources/lang/sl-SI/sessions.php resources/lang/sl/sessions.php
+1 -1
resources/lang/sl-SI/sort.php resources/lang/sl/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Ustvarjeno', 36 36 'feature_votes' => 'Star prioriteta', 37 37 'new' => 'Zadnji odgovor', 38 38 ],
resources/lang/sl-SI/store.php resources/lang/sl/store.php
resources/lang/sl-SI/supporter_tag.php resources/lang/sl/supporter_tag.php
resources/lang/sl-SI/tournament.php resources/lang/sl/tournament.php
resources/lang/sl-SI/user_verification.php resources/lang/sl/user_verification.php
resources/lang/sl-SI/users.php resources/lang/sl/users.php
resources/lang/sl-SI/validation.php resources/lang/sl/validation.php
resources/lang/sl-SI/wiki.php resources/lang/sl/wiki.php
resources/lang/sr-SP/accounts.php resources/lang/sr/accounts.php
resources/lang/sr-SP/admin.php resources/lang/sr/admin.php
resources/lang/sr-SP/api.php resources/lang/sr/api.php
resources/lang/sr-SP/artist.php resources/lang/sr/artist.php
+1 -1
resources/lang/sr-SP/authorization.php resources/lang/sr/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Ова мапа је закључана за дискусију.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Не можете да промените метаподатке номиноване мапе. Контактирајте члана БН или НАТ групе ако мислите да су подаци нетачни.',
resources/lang/sr-SP/bbcode.php resources/lang/sr/bbcode.php
resources/lang/sr-SP/beatmap_discussion_posts.php resources/lang/sr/beatmap_discussion_posts.php
resources/lang/sr-SP/beatmap_discussions.php resources/lang/sr/beatmap_discussions.php
+2 -1
resources/lang/sr-SP/beatmappacks.php resources/lang/sr/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Већ спремне колекције мапа које су засноване око неке теме.', 9 + 'empty' => '', 9 10 'nav_title' => 'листинг', 10 11 'title' => 'Колекција мапа', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Извођач/Албум', 36 37 'chart' => 'Сезонске колекције', 37 - 'featured' => '', 38 + 'featured' => 'Истакнут Уметник', 38 39 'standard' => 'Стандардне колекције', 39 40 'theme' => 'Тематске колекције', 40 41 ],
+1
resources/lang/sr-SP/beatmaps.php resources/lang/sr/beatmaps.php
··· 200 200 201 201 'rank_estimate' => [ 202 202 '_' => 'Процењује се да ће мапа бити померена у "ranked" секцију :date ако се не пронађу проблеми. Мапа је тренутно #:position у :queue.', 203 + 'on' => '', 203 204 'queue' => 'ред за "ranked" секцију', 204 205 'soon' => 'ускоро', 205 206 ],
resources/lang/sr-SP/beatmapset_discussion_votes.php resources/lang/sr/beatmapset_discussion_votes.php
resources/lang/sr-SP/beatmapset_events.php resources/lang/sr/beatmapset_events.php
resources/lang/sr-SP/beatmapset_watches.php resources/lang/sr/beatmapset_watches.php
+2 -2
resources/lang/sr-SP/beatmapsets.php resources/lang/sr/beatmapsets.php
··· 66 66 'discussion' => 'Дискусија', 67 67 68 68 'deleted_banner' => [ 69 - 'title' => '', 70 - 'message' => '', 69 + 'title' => 'Ова мапа је избрисана.', 70 + 'message' => '(ово могу да виде само модератори)', 71 71 ], 72 72 73 73 'details' => [
resources/lang/sr-SP/changelog.php resources/lang/sr/changelog.php
resources/lang/sr-SP/chat.php resources/lang/sr/chat.php
resources/lang/sr-SP/client_verifications.php resources/lang/sr/client_verifications.php
resources/lang/sr-SP/comments.php resources/lang/sr/comments.php
resources/lang/sr-SP/common.php resources/lang/sr/common.php
resources/lang/sr-SP/community.php resources/lang/sr/community.php
resources/lang/sr-SP/contest.php resources/lang/sr/contest.php
resources/lang/sr-SP/errors.php resources/lang/sr/errors.php
resources/lang/sr-SP/events.php resources/lang/sr/events.php
resources/lang/sr-SP/follows.php resources/lang/sr/follows.php
+1 -1
resources/lang/sr-SP/forum.php resources/lang/sr/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Форуми', 29 29 'latest_post' => 'Најновији Постови', 30 30 31 31 'index' => [
resources/lang/sr-SP/friends.php resources/lang/sr/friends.php
resources/lang/sr-SP/help.php resources/lang/sr/help.php
+2 -2
resources/lang/sr-SP/home.php resources/lang/sr/home.php
··· 43 43 'label' => [ 44 44 'forum' => 'тражите на форуму', 45 45 'forum_children' => 'укључујући субфоруме', 46 - 'include_deleted' => '', 46 + 'include_deleted' => 'укључи избрисане постове', 47 47 'topic_id' => 'тема #', 48 48 'username' => 'аутор', 49 49 ], ··· 84 84 'macos-fallback' => 'macOS корисници', 85 85 'mirror' => 'алтернативни линк', 86 86 'or' => 'или', 87 - 'os_version_or_later' => '', 87 + 'os_version_or_later' => ':os_version или слично', 88 88 'other_os' => 'друге платформе', 89 89 'quick_start_guide' => 'водич за брзи почетак', 90 90 'tagline' => "Почнимо!",
resources/lang/sr-SP/layout.php resources/lang/sr/layout.php
resources/lang/sr-SP/livestreams.php resources/lang/sr/livestreams.php
resources/lang/sr-SP/mail.php resources/lang/sr/mail.php
resources/lang/sr-SP/matches.php resources/lang/sr/matches.php
resources/lang/sr-SP/model_validation.php resources/lang/sr/model_validation.php
resources/lang/sr-SP/model_validation/fulfillments.php resources/lang/sr/model_validation/fulfillments.php
resources/lang/sr-SP/model_validation/payments.php resources/lang/sr/model_validation/payments.php
resources/lang/sr-SP/model_validation/store/product.php resources/lang/sr/model_validation/store/product.php
resources/lang/sr-SP/multiplayer.php resources/lang/sr/multiplayer.php
resources/lang/sr-SP/news.php resources/lang/sr/news.php
+7 -7
resources/lang/sr-SP/notifications.php resources/lang/sr/notifications.php
··· 14 14 'verifying' => 'Молимо Вас да верификујете сесију да би сте видели нотификације', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'све', 18 + 'beatmapset' => 'мапе', 19 + 'build' => 'верзије', 20 + 'channel' => 'ћаскање', 21 + 'forum_topic' => 'форум', 22 + 'news_post' => 'новости', 23 + 'user' => 'профил', 24 24 ], 25 25 26 26 'filters' => [
resources/lang/sr-SP/oauth.php resources/lang/sr/oauth.php
resources/lang/sr-SP/page_title.php resources/lang/sr/page_title.php
resources/lang/sr-SP/password_reset.php resources/lang/sr/password_reset.php
resources/lang/sr-SP/paypal/errors.php resources/lang/sr/paypal/errors.php
resources/lang/sr-SP/quick_search.php resources/lang/sr/quick_search.php
resources/lang/sr-SP/rankings.php resources/lang/sr/rankings.php
resources/lang/sr-SP/report.php resources/lang/sr/report.php
resources/lang/sr-SP/scores.php resources/lang/sr/scores.php
resources/lang/sr-SP/sessions.php resources/lang/sr/sessions.php
+1 -1
resources/lang/sr-SP/sort.php resources/lang/sr/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Креирано', 36 36 'feature_votes' => 'Приоритет по звездицама', 37 37 'new' => 'Последњи одговор', 38 38 ],
resources/lang/sr-SP/store.php resources/lang/sr/store.php
resources/lang/sr-SP/supporter_tag.php resources/lang/sr/supporter_tag.php
resources/lang/sr-SP/tournament.php resources/lang/sr/tournament.php
resources/lang/sr-SP/user_verification.php resources/lang/sr/user_verification.php
resources/lang/sr-SP/users.php resources/lang/sr/users.php
resources/lang/sr-SP/validation.php resources/lang/sr/validation.php
resources/lang/sr-SP/wiki.php resources/lang/sr/wiki.php
+1
resources/lang/sv/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Färdigförpackade samlingar med beatmaps som är baserade på ett gemensamt tema.', 9 + 'empty' => '', 9 10 'nav_title' => 'listning', 10 11 'title' => 'Beatmap-samlingar', 11 12
+1
resources/lang/sv/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Denna beatmap uppskattas vara rankad :date, så länge inga fel uppstår. Den är #:position i :queue.', 202 + 'on' => '', 202 203 'queue' => 'rankkö', 203 204 'soon' => 'snart', 204 205 ],
+3 -3
resources/lang/sv/rankings.php
··· 23 23 'multiplayer' => 'flerspelarläge', 24 24 'performance' => 'prestation', 25 25 'score' => 'poäng', 26 - 'seasons' => '', 26 + 'seasons' => 'säsonger', 27 27 ], 28 28 29 29 'seasons' => [ 30 - 'empty' => '', 31 - 'ongoing' => '', 30 + 'empty' => 'Det finns inga rum i denna säsong ännu.', 31 + 'ongoing' => 'Denna säsong pågår fortfarande (det kommer läggas till fler spellistor).', 32 32 'room_count' => '', 33 33 'url' => '', 34 34 ],
+1
resources/lang/tg-TJ/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '', 9 + 'empty' => '', 9 10 'nav_title' => '', 10 11 'title' => '', 11 12
+1
resources/lang/tg-TJ/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '', 202 + 'on' => '', 202 203 'queue' => '', 203 204 'soon' => '', 204 205 ],
+1
resources/lang/th/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'แพ็คคอลเล็คชั่นของเพลงที่อยู่ในประเภทเดียวกัน', 9 + 'empty' => '', 9 10 'nav_title' => 'รายการ', 10 11 'title' => 'แพ็คเกจบีทแมพ', 11 12
+1
resources/lang/th/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'แมพนี้จะถูก ranked ใน :date ถ้าไม่พบเจอปัญหาเพิ่มเติม แมพนี้อยู่ที่ #:position ใน :queue', 202 + 'on' => '', 202 203 'queue' => 'คิวการ rank', 203 204 'soon' => 'เร็ว ๆ นี้', 204 205 ],
+1
resources/lang/tr/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Benzer bir tema etrafında toplanmış, önceden paketlenmiş beatmap paketleri.', 9 + 'empty' => '', 9 10 'nav_title' => 'katalog', 10 11 'title' => 'Beatmap Paketleri', 11 12
+1
resources/lang/tr/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Herhangi bir sorun bulunmazsa mapin tahminen dereceli olacağı vakit :date. :queue :position. sırada bulunuyor.', 202 + 'on' => '', 202 203 'queue' => 'Derecelendirme sırasında', 203 204 'soon' => 'çok yakın', 204 205 ],
+1 -1
resources/lang/uk/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => 'Ця мапа закрита для обговорень.', 57 57 58 58 'metadata' => [ 59 59 'nominated' => 'Ви не можете змінювати метадані номінованій карті. Зв\'яжіться з членом BN або NAT, якщо ви думаєте, що вони вказані невірно.',
+2 -1
resources/lang/uk/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Попередньо впаковані колекції мап, засновані на загальних тематиках.', 9 + 'empty' => '', 9 10 'nav_title' => 'список', 10 11 'title' => 'Збірки мап', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => 'Виконавці/Альбоми', 36 37 'chart' => 'Чарти', 37 - 'featured' => '', 38 + 'featured' => 'Обраний виконавець', 38 39 'standard' => 'Стандартні', 39 40 'theme' => 'Тематичні', 40 41 ],
+1
resources/lang/uk/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Ця мапа стане рейтинговою :date, якщо ніяких проблем не буде знайдено. Вона #:position в :queue.', 202 + 'on' => '', 202 203 'queue' => 'черзі на рейтинг', 203 204 'soon' => 'дуже скоро', 204 205 ],
+1 -1
resources/lang/uk/events.php
··· 15 15 'rank' => '<strong><em>:user</em></strong> отримав #:rank місце на мапі <em>:beatmap</em> (:mode)', 16 16 'rank_lost' => '<strong><em>:user</em></strong> втратив лідерство на мапі <em>:beatmap</em> (:mode)', 17 17 'user_support_again' => '<strong>:user</strong> вирішив знову підтримати osu! Дякуємо за вашу підтримку!', 18 - 'user_support_first' => '<strong>:user</strong> став osu!прихильником! Дякуємо за вашу підтримку!', 18 + 'user_support_first' => '<strong>:user</strong> підтримав osu! - дякуємо за вашу підтримку!', 19 19 'user_support_gift' => '<strong>:user</strong> отримав тег osu!прихильника в подарунок!', 20 20 'username_change' => '<strong>:previousUsername</strong> змінив своє ім\'я на <strong><em>:user</em></strong>!', 21 21
+2 -2
resources/lang/uk/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => 'Форуми', 29 29 'latest_post' => 'Останній пост', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => 'Відновити відповідь?', 49 49 'edited' => 'Востаннє відредаговано :user :when, загалом відредаговано :count_delimited раз.|Востаннє відредаговано :user :when, загалом відредаговано :count_delimited разів.', 50 50 'posted_at' => 'написано :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => 'опубліковано :username у :forum', 52 52 53 53 'actions' => [ 54 54 'destroy' => 'Видалити відповідь',
+7 -7
resources/lang/uk/notifications.php
··· 14 14 'verifying' => 'Будь ласка, перевірте сеанс, щоб переглянути сповіщення', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => 'всі', 18 + 'beatmapset' => 'мапи', 19 + 'build' => 'збірки', 20 + 'channel' => 'чат', 21 + 'forum_topic' => 'форум', 22 + 'news_post' => 'новини', 23 + 'user' => 'профіль', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/uk/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => 'Створено', 36 36 'feature_votes' => 'Кількість голосів', 37 37 'new' => 'Остання відповідь', 38 38 ],
+1 -1
resources/lang/uk/users.php
··· 157 157 ], 158 158 'restricted_banner' => [ 159 159 'title' => 'Ваш обліковий запис заблоковано!', 160 - 'message' => 'Під час блокування вашого облікового запису, ви не зможете взаємодіяти з іншими гравцями, і ваші результати будуть показуватись лише для вас. Зазвичай, цей процес відбувається автоматично і як правило, ці обмеження облікового запису знімаються протягом доби. :link', 160 + 'message' => 'Під час блокування вашого облікового запису, ви не в змозі взаємодіяти з іншими гравцями, й ваші результати будуть показуватися лише вам. Зазвичай, цей автоматизований процес й, як правило, обмеження облікового запису знімаються протягом доби. :link', 161 161 'message_link' => 'Натисніть сюди, щоб дізнатися подробиці.', 162 162 ], 163 163 'show' => [
+1
resources/lang/vi/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => 'Sưu tầm những map được đóng gói sẵn dựa trên chủ đề chung.', 9 + 'empty' => '', 9 10 'nav_title' => 'danh sách', 10 11 'title' => 'Gói Beatmap', 11 12
+1
resources/lang/vi/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => 'Map này ước tính sẽ được Xếp Hạng :date nếu không tìm ra lỗi nào. Nó đang ở #:position trong :queue.', 202 + 'on' => '', 202 203 'queue' => 'hàng chờ xếp hạng', 203 204 'soon' => 'sớm', 204 205 ],
+1
resources/lang/zh-tw/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '相同主題的圖譜合集壓縮檔', 9 + 'empty' => '', 9 10 'nav_title' => '列表', 10 11 'title' => '曲包', 11 12
+1
resources/lang/zh-tw/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '若沒找到問題,該圖譜將於 :date 進榜。位於 :queue 中的 #:position。', 202 + 'on' => '', 202 203 'queue' => 'ranking 列隊', 203 204 'soon' => '不久後', 204 205 ],
+1 -1
resources/lang/zh-tw/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => '論壇', 29 29 'latest_post' => '最新貼文', 30 30 31 31 'index' => [
+6 -6
resources/lang/zh-tw/notifications.php
··· 15 15 16 16 'action_type' => [ 17 17 '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 18 + 'beatmapset' => '圖譜', 19 + 'build' => '版本', 20 + 'channel' => '聊天', 21 + 'forum_topic' => '論壇', 22 + 'news_post' => '新聞', 23 + 'user' => '個人資料', 24 24 ], 25 25 26 26 'filters' => [
+1 -1
resources/lang/zh/authorization.php
··· 53 53 ], 54 54 55 55 'beatmapset' => [ 56 - 'discussion_locked' => '', 56 + 'discussion_locked' => '谱面已上锁,以供讨论。', 57 57 58 58 'metadata' => [ 59 59 'nominated' => '您不能更改已提名谱面的信息。如果您认为其不正确,请联系谱面审核成员 (BN) 或审核评估团队 (NAT) 更改。',
+2 -1
resources/lang/zh/beatmappacks.php
··· 6 6 return [ 7 7 'index' => [ 8 8 'description' => '围绕某个相同主题打包好的曲包', 9 + 'empty' => '', 9 10 'nav_title' => '列表', 10 11 'title' => '曲包', 11 12 ··· 34 35 'mode' => [ 35 36 'artist' => '艺术家/专辑', 36 37 'chart' => '聚光灯', 37 - 'featured' => '', 38 + 'featured' => '精选艺术家', 38 39 'standard' => '常规', 39 40 'theme' => '主题', 40 41 ],
+1
resources/lang/zh/beatmaps.php
··· 199 199 200 200 'rank_estimate' => [ 201 201 '_' => '谱面正位于 :queue 中第 :position 位。如果没有问题,谱面将在 :date 上架 (Ranked)。', 202 + 'on' => '', 202 203 'queue' => '谱面上架队列', 203 204 'soon' => '不久后', 204 205 ],
+2 -2
resources/lang/zh/forum.php
··· 25 25 ], 26 26 27 27 'forums' => [ 28 - 'forums' => '', 28 + 'forums' => '论坛', 29 29 'latest_post' => '最新帖子', 30 30 31 31 'index' => [ ··· 48 48 'confirm_restore' => '恢复此回复?', 49 49 'edited' => '最后一次由 :user 在 :when 编辑,总共编辑了 :count_delimited 次。', 50 50 'posted_at' => '发表于 :when', 51 - 'posted_by_in' => '', 51 + 'posted_by_in' => ':username 在 “:forum” 下发帖', 52 52 53 53 'actions' => [ 54 54 'destroy' => '删除回复',
+7 -7
resources/lang/zh/notifications.php
··· 14 14 'verifying' => '请验证会话以查看通知', 15 15 16 16 'action_type' => [ 17 - '_' => '', 18 - 'beatmapset' => '', 19 - 'build' => '', 20 - 'channel' => '', 21 - 'forum_topic' => '', 22 - 'news_post' => '', 23 - 'user' => '', 17 + '_' => '全部', 18 + 'beatmapset' => '谱面', 19 + 'build' => '版本', 20 + 'channel' => '聊天', 21 + 'forum_topic' => '论坛', 22 + 'news_post' => '新闻', 23 + 'user' => '个人资料', 24 24 ], 25 25 26 26 'filters' => [
+4 -4
resources/lang/zh/rankings.php
··· 23 23 'multiplayer' => '多人游戏', 24 24 'performance' => '表现', 25 25 'score' => '总分', 26 - 'seasons' => '季', 26 + 'seasons' => '季赛', 27 27 ], 28 28 29 29 'seasons' => [ 30 - 'empty' => '这一季还没有房间。', 31 - 'ongoing' => '这一季仍在进行中(将加入更多歌单)。', 30 + 'empty' => '本季赛暂无可用房间。', 31 + 'ongoing' => '本季赛仍在进行中(将加入更多歌单)。', 32 32 'room_count' => '歌单数量', 33 - 'url' => '显示该季更多信息。', 33 + 'url' => '显示该季赛更多信息。', 34 34 ], 35 35 36 36 'spotlight' => [
+1 -1
resources/lang/zh/sort.php
··· 32 32 ], 33 33 34 34 'forum_topics' => [ 35 - 'created' => '', 35 + 'created' => '已创建', 36 36 'feature_votes' => '星级优先级', 37 37 'new' => '最后回复', 38 38 ],
+1 -1
resources/lang/zh/users.php
··· 166 166 'first_members' => '元老玩家', 167 167 'is_developer' => 'osu! 开发者', 168 168 'is_supporter' => 'osu! 支持者', 169 - 'joined_at' => '注册时间::date', 169 + 'joined_at' => '注册于 :date', 170 170 'lastvisit' => '最后活跃::date', 171 171 'lastvisit_online' => '当前在线', 172 172 'missingtext' => '你可能打错字了!(或者该用户已经被封禁)',
+8
resources/views/docs/info.md.blade.php
··· 18 18 19 19 Current rate limit is set at an insanely high 1200 requests per minute, with burst capability of up to 200 beyond that. If you require more, you probably fall into the above category of abuse. If you are doing more than 60 requests a minute, you should probably give [peppy](mailto:pe@ppy.sh) a yell. 20 20 21 + # Wrappers 22 + 23 + Below is a list of some language-specific wrappers maintained by the community. Your mileage may vary when using them – please report any issues to the wrapper first before reporting back to us. 24 + 25 + - [ossapi](https://github.com/circleguard/ossapi) (python) 26 + - [aiosu](https://github.com/NiceAesth/aiosu) (python) 27 + - [rosu-v2](https://github.com/MaxOhn/rosu-v2) (rust) 28 + 21 29 # Changelog 22 30 23 31 For a full list of changes, see the
+96 -82
resources/views/forum/topics/_post.blade.php
··· 3 3 See the LICENCE file in the repository root for full licence text. 4 4 --}} 5 5 <?php 6 - $options['postPosition'] = $options['postPosition'] ?? 1; 7 - $options['signature'] = $options['signature'] ?? true; 8 - 9 - $options['buttons']['delete'] = $options['buttons']['delete'] ?? false; 10 - $options['buttons']['edit'] = $options['buttons']['edit'] ?? false; 11 - $options['buttons']['quote'] = $options['buttons']['quote'] ?? false; 12 - $options['buttons']['report'] = auth()->check() && $post->poster_id !== auth()->user()->getKey(); 13 - 14 - $buttons = []; 15 - 16 - foreach (['edit', 'delete', 'quote', 'report'] as $buttonType) { 17 - if ($options['buttons'][$buttonType]) { 18 - $buttons[] = $buttonType; 19 - } 20 - } 6 + $options['postPosition'] ??= 1; 21 7 22 8 $user = $post->userNormalized(); 23 - $hidden = $post->trashed() || ($options['postPosition'] === 1 && $post->topic->trashed()); 9 + $blocked = $currentUser !== null && $currentUser->hasBlocked($user); 10 + $hidden = $blocked || $post->trashed() || ($options['postPosition'] === 1 && $post->topic->trashed()); 24 11 ?> 25 12 <div 26 13 {{-- js-forum-post is also used by js-forum-post-report for the postId and postUsername dataset --}} ··· 29 16 data-post-username="{{ $user->username }}" 30 17 data-post-position="{{ $options["postPosition"] }}" 31 18 > 32 - @include('forum.topics._post_info', compact('user')) 19 + @if ($blocked) 20 + <div class="forum-post-info"> 21 + {!! link_to_user($user, null, null, ['forum-post-info__row', 'forum-post-info__row--username']) !!} 22 + </div> 23 + <div class="forum-post__body"> 24 + <div class="forum-post__content forum-post__content--blocked"> 25 + {!! osu_trans('users.blocks.forum_post_text') !!} 26 + </div> 27 + </div> 28 + @else 29 + @php 30 + $options['signature'] ??= true; 33 31 34 - <div class="forum-post__body js-forum-post-edit--container"> 35 - <div class="forum-post__content forum-post__content--header"> 36 - <div class="forum-post__header-content"> 37 - @if (isset($topic) && $topic->topic_poster === $post->poster_id) 38 - <div class="forum-post__header-content-item"> 39 - <span class="forum-user-badge"> 40 - {{ osu_trans('forum.post.info.topic_starter') }} 41 - </span> 42 - </div> 43 - @endif 32 + $options['buttons']['delete'] ??= false; 33 + $options['buttons']['edit'] ??= false; 34 + $options['buttons']['quote'] ??= false; 35 + $options['buttons']['report'] = $currentUserId !== null && $post->poster_id !== $currentUserId; 44 36 45 - <div class="forum-post__header-content-item"> 46 - {!! link_to_user($user, null, '', ['forum-post__user']) !!} 37 + $buttons = []; 47 38 48 - <a class="js-post-url" rel="nofollow" href="{{ $post->exists ? route('forum.posts.show', $post->post_id) : '#' }}"> 49 - {!! timeago($post->post_time) !!} 50 - </a> 51 - </div> 52 - </div> 39 + foreach (['edit', 'delete', 'quote', 'report'] as $buttonType) { 40 + if ($options['buttons'][$buttonType]) { 41 + $buttons[] = $buttonType; 42 + } 43 + } 44 + @endphp 45 + @include('forum.topics._post_info', compact('user')) 53 46 54 - @if (count($buttons) > 0) 55 - <div class="forum-post__menu"> 56 - @php 57 - $menuId = implode(':', ['forum-post', $post->getKey(), rand()]); 58 - @endphp 59 - <button class="forum-post__menu-button js-click-menu" data-click-menu-target="{{ $menuId }}"> 60 - <span class="fas fa-ellipsis-v"></span> 61 - </button> 47 + <div class="forum-post__body js-forum-post-edit--container"> 48 + <div class="forum-post__content forum-post__content--header"> 49 + <div class="forum-post__header-content"> 50 + @if (isset($topic) && $topic->topic_poster === $post->poster_id) 51 + <div class="forum-post__header-content-item"> 52 + <span class="forum-user-badge"> 53 + {{ osu_trans('forum.post.info.topic_starter') }} 54 + </span> 55 + </div> 56 + @endif 62 57 63 - <div 64 - class="simple-menu simple-menu--forum-list js-click-menu" 65 - data-visibility="hidden" 66 - data-click-menu-id="{{ $menuId }}" 67 - > 68 - @foreach ($buttons as $button) 69 - @include("forum.posts._button_{$button}", [ 70 - 'class' => 'simple-menu__item', 71 - 'post' => $post, 72 - 'type' => 'menu', 73 - ]) 74 - @endforeach 58 + <div class="forum-post__header-content-item"> 59 + {!! link_to_user($user, null, '', ['forum-post__user']) !!} 60 + 61 + <a class="js-post-url" rel="nofollow" href="{{ $post->exists ? route('forum.posts.show', $post->post_id) : '#' }}"> 62 + {!! timeago($post->post_time) !!} 63 + </a> 75 64 </div> 76 65 </div> 77 - @endif 78 - </div> 66 + 67 + @if (count($buttons) > 0) 68 + <div class="forum-post__menu"> 69 + @php 70 + $menuId = implode(':', ['forum-post', $post->getKey(), rand()]); 71 + @endphp 72 + <button class="forum-post__menu-button js-click-menu" data-click-menu-target="{{ $menuId }}"> 73 + <span class="fas fa-ellipsis-v"></span> 74 + </button> 79 75 80 - <div class="forum-post__content forum-post__content--main"> 81 - <div class="forum-post-content {{ $options['contentExtraClasses'] ?? '' }} js-audio--group"> 82 - {!! $post->bodyHTML() !!} 76 + <div 77 + class="simple-menu simple-menu--forum-list js-click-menu" 78 + data-visibility="hidden" 79 + data-click-menu-id="{{ $menuId }}" 80 + > 81 + @foreach ($buttons as $button) 82 + @include("forum.posts._button_{$button}", [ 83 + 'class' => 'simple-menu__item', 84 + 'post' => $post, 85 + 'type' => 'menu', 86 + ]) 87 + @endforeach 88 + </div> 89 + </div> 90 + @endif 83 91 </div> 84 - </div> 85 92 86 - @if($post->post_edit_count > 0) 87 - <div class="forum-post__content forum-post__content--footer"> 88 - {!! 89 - osu_trans_choice('forum.post.edited', $post->post_edit_count, [ 90 - 'user' => link_to_user($post->lastEditorNormalized(), null, '', []), 91 - 'when' => timeago($post->post_edit_time), 92 - ]) 93 - !!} 93 + <div class="forum-post__content forum-post__content--main"> 94 + <div class="forum-post-content {{ $options['contentExtraClasses'] ?? '' }} js-audio--group"> 95 + {!! $post->bodyHTML() !!} 96 + </div> 94 97 </div> 95 - @endif 96 98 97 - @if($options["signature"] !== false && present($post->userNormalized()->user_sig)) 98 - <div class="forum-post__content forum-post__content--signature js-audio--group hidden-xs"> 99 - {!! bbcode($post->userNormalized()->user_sig, $post->userNormalized()->user_sig_bbcode_uid) !!} 100 - </div> 101 - @endif 99 + @if($post->post_edit_count > 0) 100 + <div class="forum-post__content forum-post__content--footer"> 101 + {!! 102 + osu_trans_choice('forum.post.edited', $post->post_edit_count, [ 103 + 'user' => link_to_user($post->lastEditorNormalized(), null, '', []), 104 + 'when' => timeago($post->post_edit_time), 105 + ]) 106 + !!} 107 + </div> 108 + @endif 102 109 103 - @if (count($buttons) > 0) 104 - <div class="forum-post__actions"> 105 - @foreach ($buttons as $button) 106 - @include("forum.posts._button_{$button}", ['post' => $post, 'type' => 'circle']) 107 - @endforeach 108 - </div> 109 - @endif 110 - </div> 110 + @if($options["signature"] !== false && null !== ($signature = $userSignatures->get($user))) 111 + <div class="forum-post__content forum-post__content--signature js-audio--group hidden-xs"> 112 + {!! $signature !!} 113 + </div> 114 + @endif 115 + 116 + @if (count($buttons) > 0) 117 + <div class="forum-post__actions"> 118 + @foreach ($buttons as $button) 119 + @include("forum.posts._button_{$button}", ['post' => $post, 'type' => 'circle']) 120 + @endforeach 121 + </div> 122 + @endif 123 + </div> 124 + @endif 111 125 </div>
+9
resources/views/forum/topics/_posts.blade.php
··· 3 3 See the LICENCE file in the repository root for full licence text. 4 4 --}} 5 5 @php 6 + use App\Libraries\User\UserSignatures; 7 + 8 + $currentUser = Auth::user(); 9 + $currentUserId = $currentUser?->getKey(); 10 + $userSignatures = new UserSignatures(); 11 + 6 12 $postPosition = $firstPostPosition; 7 13 @endphp 8 14 ··· 22 28 $isBeatmapsetPost = $postPosition === 1 && $post->isBeatmapsetPost(); 23 29 @endphp 24 30 @include('forum.topics._post', [ 31 + 'currentUser' => $currentUser, 32 + 'currentUserId' => $currentUserId, 25 33 'post' => $post, 34 + 'userSignatures' => $userSignatures, 26 35 'options' => [ 27 36 'postPosition' => $postPosition, 28 37 'signature' => $topic->forum->enable_sigs,
+8 -5
resources/views/home/_user_news_post_preview.blade.php
··· 2 2 Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 3 3 See the LICENCE file in the repository root for full licence text. 4 4 --}} 5 - 5 + @php 6 + $publishedAt = $post->published_at; 7 + @endphp 6 8 <div class="news-post-preview{{$collapsed ? ' news-post-preview--collapsed' : ''}}"> 7 9 <a 8 10 class="news-post-preview__image" ··· 10 12 {!! background_image($post->firstImage()) !!} 11 13 ></a> 12 14 <div class="news-post-preview__body"> 13 - <div class="news-post-preview__post-date js-tooltip-time" title="{{ json_time($post->published_at) }}"> 15 + <div class="news-post-preview__post-date js-tooltip-time" title="{{ json_time($publishedAt) }}"> 14 16 <div class="news-post-preview__date"> 15 - {{$post->published_at->format('d')}} 17 + {{ i18n_date_auto($publishedAt, 'd') }} 16 18 </div> 19 + 17 20 <div class="news-post-preview__month-year"> 18 21 @if ($collapsed) 19 - &nbsp;{{$post->published_at->format('M')}} 22 + &nbsp;{{ i18n_date_auto($publishedAt, 'MMM') }} 20 23 @else 21 - {{$post->published_at->format('M Y')}} 24 + {{ i18n_date_auto($publishedAt, 'yMMM') }} 22 25 @endif 23 26 </div> 24 27 </div>
+1 -1
resources/views/objects/search/_forum_options.blade.php
··· 71 71 {{ osu_trans('home.search.forum_post.all') }} 72 72 </option> 73 73 74 - @foreach (App\Models\Forum\Forum::displayList()->get() as $forum) 74 + @foreach (App\Models\Forum\Forum::searchable()->displayList()->get() as $forum) 75 75 @if (priv_check('ForumView', $forum)->can()) 76 76 <option 77 77 value="{{ $forum->getKey() }}"
+3
resources/views/packs/index.blade.php
··· 18 18 </ul> 19 19 20 20 <div class="beatmap-packs js-accordion"> 21 + @if (count($packs) === 0) 22 + {{ osu_trans('beatmappacks.index.empty') }} 23 + @endif 21 24 @foreach ($packs as $pack) 22 25 <div class="beatmap-pack js-beatmap-pack js-accordion__item" data-pack-tag="{{ $pack->tag }}"> 23 26 <a href="{{ route('packs.show', $pack) }}" class="beatmap-pack__header js-accordion__item-header">
+5 -5
tests/Browser/SanityTest.php
··· 129 129 ]); 130 130 131 131 // factories for /beatmapsets/* 132 - self::$scaffolding['beatmap_mirror'] = factory(BeatmapMirror::class)->create(); 132 + self::$scaffolding['beatmap_mirror'] = BeatmapMirror::factory()->create(); 133 133 self::$scaffolding['genre'] = Genre::factory()->create(); 134 134 self::$scaffolding['language'] = Language::factory()->create(); 135 135 self::$scaffolding['beatmapset'] = Beatmapset::factory()->create([ ··· 156 156 self::$scaffolding['pack'] = BeatmapPack::factory()->create(); 157 157 158 158 // factories for /community/contests/* 159 - self::$scaffolding['contest'] = factory(Contest::class)->states('entry')->create(); 159 + self::$scaffolding['contest'] = Contest::factory()->entry()->create(); 160 160 161 161 // factories for /community/tournaments/* 162 162 self::$scaffolding['tournament'] = Tournament::factory()->create(); 163 163 164 164 // factories for /beatmaps/artists/* 165 - self::$scaffolding['artist'] = factory(Artist::class)->create(); 165 + self::$scaffolding['artist'] = Artist::factory()->create(); 166 166 self::$scaffolding['track'] = ArtistTrack::factory()->create([ 167 167 'artist_id' => self::$scaffolding['artist']->getKey(), 168 168 ]); ··· 221 221 222 222 // factory for /home/changelog/* 223 223 self::$scaffolding['stream'] = factory(UpdateStream::class)->create(); 224 - self::$scaffolding['changelog'] = factory(Changelog::class)->create([ 224 + self::$scaffolding['changelog'] = Changelog::factory()->create([ 225 225 'stream_id' => self::$scaffolding['stream']->stream_id, 226 226 ]); 227 227 self::$scaffolding['build'] = Build::factory()->create([ ··· 229 229 ]); 230 230 231 231 // factory for /g/* 232 - self::$scaffolding['group'] = factory(Group::class)->create(); 232 + self::$scaffolding['group'] = Group::factory()->create(); 233 233 234 234 // factory for comments 235 235 self::$scaffolding['comment'] = Comment::factory()->create([
+1 -1
tests/Controllers/Chat/Channels/MessagesControllerTest.php
··· 166 166 'user' => $this->user->getKey(), 167 167 ])); 168 168 169 - $filter = factory(ChatFilter::class)->create(); 169 + $filter = ChatFilter::factory()->create(); 170 170 171 171 $this->json( 172 172 'POST',
+26 -15
tests/Libraries/bbcode_examples/basic_list.html
··· 1 - <ol class="unordered"><li>Here 2 - <br /></li><li>be 3 - <br /></li><li>basic list 4 - <br /></li></ol><ol><li>Here 5 - <br /></li><li>be 6 - <br /></li><li>numbered list 7 - <br /></li></ol><ol class="unordered"><li>And 8 - <br /></li><li>here 9 - <br /><ol class="unordered"><li>is 10 - <br /></li><li>nested 11 - <br /></li></ol></li><li>list 12 - <br /></li><li>and 13 - <br /><ol><li>with 14 - <br /></li><li>number 15 - <br /></li></ol></li></ol> 1 + <ol class="unordered"> 2 + <li>Here</li> 3 + <li>be</li> 4 + <li>basic list</li> 5 + </ol> 6 + <ol> 7 + <li>Here</li> 8 + <li>be</li> 9 + <li>numbered list</li> 10 + </ol> 11 + <ol class="unordered"> 12 + <li>And</li> 13 + <li>here<br /> 14 + <ol class="unordered"> 15 + <li>is</li> 16 + <li>nested</li> 17 + </ol> 18 + </li> 19 + <li>list</li> 20 + <li>and<br /> 21 + <ol> 22 + <li>with</li> 23 + <li>number</li> 24 + </ol> 25 + </li> 26 + </ol>
+9 -3
tests/Libraries/bbcode_examples/basic_quote.html
··· 1 - <blockquote>Basic 2 - <br />quote 3 - <br />tag</blockquote><blockquote><h4>name wrote:</h4>And with name</blockquote> 1 + <blockquote> 2 + Basic<br /> 3 + quote<br /> 4 + tag 5 + </blockquote> 6 + <blockquote> 7 + <h4>name wrote:</h4> 8 + And with name 9 + </blockquote>
+11
tests/Libraries/bbcode_examples/box_with_bbcode_title.base.txt
··· 1 + [box=[color=red]colored box title[/color]]box content[/box] 2 + 3 + [size=50]other content[/size] 4 + 5 + [box=[\[] weird \[ title]box content[/box] 6 + 7 + [size=50]other content 2[/size] 8 + 9 + [box=[weird title]broken box content[/box] 10 + 11 + [size=50]other content 3[/size]
+11
tests/Libraries/bbcode_examples/box_with_bbcode_title.db.txt
··· 1 + [box=[color=red:1]colored box title[/color:1]:1]box content[/box:1] 2 + 3 + [size=50:1]other content[/size:1] 4 + 5 + [box=[\[] weird \[ title:1]box content[/box:1] 6 + 7 + [size=50:1]other content 2[/size:1] 8 + 9 + [box=[weird title]broken box content[/box:1] 10 + 11 + [size=50:1]other content 3[/size:1]
+16
tests/Libraries/bbcode_examples/box_with_bbcode_title.html
··· 1 + <div class="js-spoilerbox bbcode-spoilerbox"> 2 + <button class="js-spoilerbox__link bbcode-spoilerbox__link" type="button"> 3 + <span class="bbcode-spoilerbox__link-icon"></span><span style="color:#FF0000;">colored box title</span> 4 + </button> 5 + <div class="bbcode-spoilerbox__body">box content</div> 6 + </div><br /> 7 + <span class="size-50">other content</span><br /> 8 + <br /> 9 + <div class="js-spoilerbox bbcode-spoilerbox"> 10 + <button class="js-spoilerbox__link bbcode-spoilerbox__link" type="button"><span class="bbcode-spoilerbox__link-icon"></span>[\[] weird \[ title</button> 11 + <div class="bbcode-spoilerbox__body">box content</div> 12 + </div><br /> 13 + <span class="size-50">other content 2</span><br /> 14 + <br /> 15 + [box=[weird title]broken box content<br /> 16 + <span class="size-50">other content 3</span>
+16
tests/Libraries/bbcode_examples/imagemap.base.txt
··· 1 + [imagemap] 2 + https://assets.ppy.sh/osu-web-test-resources/placeholder-1280x720.jpg 3 + 0 6.9 20 30 https://osu.ppy.sh link 1 4 + 40 50 60 70 https://osu.ppy.sh/home 5 + 8 8 8 8 # 6 + 9 9 9 9 # some title 7 + [/imagemap] 8 + 9 + [imagemap] 10 + https://assets.ppy.sh/osu-web-test-resources/placeholder-1280x720.jpg 11 + 40 50 60 70 https://osu.ppy.sh/users/2 12 + [/imagemap] 13 + 14 + [imagemap] 15 + https://assets.ppy.sh/osu-web-test-resources/placeholder-1280x720.jpg 16 + [/imagemap]
+5
tests/Libraries/bbcode_examples/imagemap.db.txt
··· 1 + [imagemap]&#10;https&#58;//assets&#46;ppy&#46;sh/osu-web-test-resources/placeholder-1280x720&#46;jpg&#10;0 6&#46;9 20 30 https&#58;//osu&#46;ppy&#46;sh link 1&#10;40 50 60 70 https&#58;//osu&#46;ppy&#46;sh/home&#10;8 8 8 8 #&#10;9 9 9 9 # some title&#10;[/imagemap] 2 + 3 + [imagemap]&#10;https&#58;//assets&#46;ppy&#46;sh/osu-web-test-resources/placeholder-1280x720&#46;jpg&#10;40 50 60 70 https&#58;//osu&#46;ppy&#46;sh/users/2&#10;[/imagemap] 4 + 5 + [imagemap]&#10;https&#58;//assets&#46;ppy&#46;sh/osu-web-test-resources/placeholder-1280x720&#46;jpg&#10;[/imagemap]
+16
tests/Libraries/bbcode_examples/imagemap.html
··· 1 + <div class="imagemap"> 2 + <img class="imagemap__image" loading="lazy" src="https://assets.ppy.sh/osu-web-test-resources/placeholder-1280x720.jpg" width="1280" height="720" alt="placeholder-1280x720.jpg" /> 3 + <a class="imagemap__link" href="https://osu.ppy.sh" style="left:0%;top:6.9%;width:20%;height:30%;" title="link 1"></a> 4 + <a class="imagemap__link" href="https://osu.ppy.sh/home" style="left:40%;top:50%;width:60%;height:70%;" title=""></a> 5 + <span class="imagemap__link" style="left:8%;top:8%;width:8%;height:8%;" title=""></span> 6 + <span class="imagemap__link" style="left:9%;top:9%;width:9%;height:9%;" title="some title"></span> 7 + </div> 8 + <br /> 9 + <div class="imagemap"> 10 + <img class="imagemap__image" loading="lazy" src="https://assets.ppy.sh/osu-web-test-resources/placeholder-1280x720.jpg" width="1280" height="720" alt="placeholder-1280x720.jpg" /> 11 + <a class="imagemap__link" href="https://osu.ppy.sh/users/2" style="left:40%;top:50%;width:60%;height:70%;" title=""></a> 12 + </div> 13 + <br /> 14 + [imagemap]<br /> 15 + https://assets.ppy.sh/osu-web-test-resources/placeholder-1280x720.jpg<br /> 16 + [/imagemap]
+16 -7
tests/Libraries/bbcode_examples/list_title.html
··· 1 - <ul class="bbcode__list-title"><li>The title<br /></li></ul><ol class="unordered"><li>Here 2 - <br /></li><li>be 3 - <br /></li><li>basic list 4 - <br /></li></ol><ul class="bbcode__list-title"><li>The title<br /></li></ul><ol><li>Here 5 - <br /></li><li>be 6 - <br /></li><li>numbered list 7 - <br /></li></ol> 1 + <ul class="bbcode__list-title"> 2 + <li>The title</li> 3 + </ul> 4 + <ol class="unordered"> 5 + <li>Here</li> 6 + <li>be</li> 7 + <li>basic list</li> 8 + </ol> 9 + <ul class="bbcode__list-title"> 10 + <li>The title</li> 11 + </ul> 12 + <ol> 13 + <li>Here</li> 14 + <li>be</li> 15 + <li>numbered list</li> 16 + </ol>
+11
tests/Libraries/bbcode_examples/multiple_links.base.txt
··· 1 + https://osu.ppy.sh https://osu.ppy.sh 2 + 3 + http://localhost/home 4 + http://localhost/home 5 + http://localhost/home 6 + 7 + www.example.com 8 + www.example.com 9 + 10 + user@example.com 11 + user@example.com
+11
tests/Libraries/bbcode_examples/multiple_links.db.txt
··· 1 + <!-- m --><a href='https://osu.ppy.sh' rel='nofollow'>https://osu.ppy.sh</a><!-- m --> <!-- m --><a href='https://osu.ppy.sh' rel='nofollow'>https://osu.ppy.sh</a><!-- m --> 2 + 3 + <!-- m --><a href='http://localhost/home' rel='nofollow'>home</a><!-- m --> 4 + <!-- m --><a href='http://localhost/home' rel='nofollow'>home</a><!-- m --> 5 + <!-- m --><a href='http://localhost/home' rel='nofollow'>home</a><!-- m --> 6 + 7 + <!-- w --><a href='http://www.example.com' rel='nofollow'>www.example.com</a><!-- w --> 8 + <!-- w --><a href='http://www.example.com' rel='nofollow'>www.example.com</a><!-- w --> 9 + 10 + <!-- e --><a href='mailto:user@example.com' rel='nofollow'>user@example.com</a><!-- e --> 11 + <!-- e --><a href='mailto:user@example.com' rel='nofollow'>user@example.com</a><!-- e -->
+11
tests/Libraries/bbcode_examples/multiple_links.html
··· 1 + <!-- m --><a href="https://osu.ppy.sh" rel="nofollow">https://osu.ppy.sh</a><!-- m --> <!-- m --><a href="https://osu.ppy.sh" rel="nofollow">https://osu.ppy.sh</a><!-- m --><br /> 2 + <br /> 3 + <!-- m --><a href="http://localhost/home" rel="nofollow">home</a><!-- m --><br /> 4 + <!-- m --><a href="http://localhost/home" rel="nofollow">home</a><!-- m --><br /> 5 + <!-- m --><a href="http://localhost/home" rel="nofollow">home</a><!-- m --><br /> 6 + <br /> 7 + <!-- w --><a href="http://www.example.com" rel="nofollow">www.example.com</a><!-- w --><br /> 8 + <!-- w --><a href="http://www.example.com" rel="nofollow">www.example.com</a><!-- w --><br /> 9 + <br /> 10 + <!-- e --><a href="mailto:user@example.com" rel="nofollow">user@example.com</a><!-- e --><br /> 11 + <!-- e --><a href="mailto:user@example.com" rel="nofollow">user@example.com</a><!-- e -->
+1 -1
tests/Middleware/RouteScopesTest.php
··· 45 45 'stream_id' => 1, // Changelog stream_id is tinyint, autoincrement makes test fail too soon. 46 46 ]); 47 47 48 - factory(Changelog::class)->create([ 48 + Changelog::factory()->create([ 49 49 'stream_id' => $stream->getKey(), 50 50 'user_id' => 1, // user doesn't need to exist and not having to create a user makes the test much faster 51 51 ]);
+2 -2
tests/Models/BeatmapsetTest.php
··· 443 443 444 444 $beatmapset = Beatmapset::factory()->create(array_merge($defaultParams, $params)); 445 445 $beatmapset->beatmaps()->save(Beatmap::factory()->make()); 446 - factory(BeatmapMirror::class)->states('default')->create(); 446 + BeatmapMirror::factory()->default()->create(); 447 447 448 448 return $beatmapset; 449 449 } ··· 464 464 foreach ($playmodes as $playmode) { 465 465 $beatmapset->beatmaps()->save(Beatmap::factory()->make(['playmode' => Beatmap::modeInt($playmode)])); 466 466 } 467 - factory(BeatmapMirror::class)->states('default')->create(); 467 + BeatmapMirror::factory()->default()->create(); 468 468 469 469 return $beatmapset; 470 470 }
+5 -5
tests/Models/ContestTest.php
··· 45 45 ]); 46 46 } 47 47 } 48 - $contest = factory(Contest::class)->create([ 48 + $contest = Contest::factory()->create([ 49 49 'extra_options' => [ 50 50 'requirement' => [ 51 51 'must_pass' => $mustPass, ··· 54 54 ], 55 55 ], 56 56 ]); 57 - $entries = factory(ContestEntry::class, 2)->create(['contest_id' => $contest->getKey()]); 57 + $entries = ContestEntry::factory()->count(2)->create(['contest_id' => $contest->getKey()]); 58 58 59 59 if (!$canVote) { 60 60 $this->expectException(InvariantException::class); ··· 89 89 90 90 public function testAssertVoteRequirementNoRequirement(): void 91 91 { 92 - $contest = factory(Contest::class)->create(); 93 - $entry = factory(ContestEntry::class)->create(['contest_id' => $contest->getKey()]); 92 + $contest = Contest::factory()->create(); 93 + $entry = ContestEntry::factory()->create(['contest_id' => $contest->getKey()]); 94 94 $user = User::factory()->create(); 95 95 96 96 $contest->assertVoteRequirement($user, $entry); ··· 105 105 $extraOptions = $showEntryUserOption === null 106 106 ? null 107 107 : ['show_entry_user' => $showEntryUserOption]; 108 - $contest = factory(Contest::class)->create([ 108 + $contest = Contest::factory()->create([ 109 109 'show_votes' => $showVotes, 110 110 'extra_options' => $extraOptions, 111 111 ]);
+2 -2
tests/Models/GroupTest.php
··· 14 14 public function testRename() 15 15 { 16 16 $newName = 'new name'; 17 - $group = factory(Group::class)->create(['group_name' => 'name']); 17 + $group = Group::factory()->create(['group_name' => 'name']); 18 18 $groupRenameEventCount = $this->getGroupRenameEventCount($group); 19 19 20 20 $group->rename($newName); ··· 26 26 public function testRenameUnchanged() 27 27 { 28 28 $name = 'name'; 29 - $group = factory(Group::class)->create(['group_name' => $name]); 29 + $group = Group::factory()->create(['group_name' => $name]); 30 30 $groupRenameEventCount = $this->getGroupRenameEventCount($group); 31 31 32 32 $group->rename($name);
+1 -1
tests/Models/ModelTest.php
··· 15 15 { 16 16 public function testGetWithHasMore() 17 17 { 18 - factory(Artist::class, 5)->create(); 18 + Artist::factory()->count(5)->create(); 19 19 $count = Artist::count(); 20 20 21 21 $limit = $count - 1;
+63 -7
tests/Models/UserReportTest.php
··· 12 12 use App\Models\BeatmapDiscussion; 13 13 use App\Models\BeatmapDiscussionPost; 14 14 use App\Models\Beatmapset; 15 + use App\Models\Chat\Message; 15 16 use App\Models\Forum; 16 17 use App\Models\Traits\ReportableInterface; 17 18 use App\Models\User; 18 19 use App\Models\UserReport; 20 + use Exception; 19 21 use Tests\TestCase; 20 22 21 23 class UserReportTest extends TestCase 22 24 { 25 + private static function getReportableUser(ReportableInterface $reportable) 26 + { 27 + return match ($reportable::class) { 28 + Message::class => $reportable->sender, 29 + User::class => $reportable, 30 + default => $reportable->user, 31 + }; 32 + } 33 + 23 34 private static function makeReportable(string $class): ReportableInterface 24 35 { 25 36 $modelFactory = $class::factory(); ··· 41 52 $userColumn = 'poster_id'; 42 53 } 43 54 44 - return $modelFactory->create([$userColumn => User::factory()]); 55 + return $class === User::class 56 + ? $modelFactory->create() 57 + : $modelFactory->create([$userColumn => User::factory()]); 45 58 } 46 59 47 60 private static function reportParams(array $additionalParams = []): array ··· 61 74 $reportable = static::makeReportable($class); 62 75 63 76 $this->expectException(ValidationException::class); 64 - $reportable->reportBy($reportable->user, static::reportParams()); 77 + $reportable->reportBy(static::getReportableUser($reportable), static::reportParams()); 65 78 } 66 79 67 80 public function testCannotReportScoreableBeatmapset() ··· 91 104 /** 92 105 * @dataProvider reportableClasses 93 106 */ 94 - public function testNoComments(string $class) 107 + public function testNoComments(string $class): void 108 + { 109 + $reportable = static::makeReportable($class); 110 + $reporter = User::factory()->create(); 111 + 112 + if ($class === Message::class) { 113 + $this->expectCountChange(fn () => UserReport::count(), 1); 114 + } else { 115 + $this->expectException(ValidationException::class); 116 + } 117 + $reportable->reportBy($reporter, static::reportParams([ 118 + 'comments' => null, 119 + ])); 120 + } 121 + 122 + /** 123 + * @dataProvider reportableClasses 124 + */ 125 + public function testNoCommentsReasonOther(string $class): void 95 126 { 96 127 $reportable = static::makeReportable($class); 97 128 $reporter = User::factory()->create(); ··· 99 130 $this->expectException(ValidationException::class); 100 131 $reportable->reportBy($reporter, static::reportParams([ 101 132 'comments' => null, 133 + 'reason' => 'Other', 102 134 ])); 103 135 } 104 136 ··· 128 160 $this->assertTrue($report->reportable->is($reportable)); 129 161 } 130 162 163 + /** 164 + * @dataProvider reportableClasses 165 + */ 166 + public function testReportableNotificationEndpoint(string $class): void 167 + { 168 + $reportable = static::makeReportable($class); 169 + $reporter = User::factory()->create(); 170 + $report = $reportable->reportBy($reporter, static::reportParams()); 171 + 172 + $report->routeNotificationForSlack(null); 173 + 174 + $this->assertTrue(true, 'should not fail getting notification routing url'); 175 + } 176 + 131 177 public function reportableClasses(): array 132 178 { 133 - return array_map( 134 - fn (string $morphName): array => [MorphMap::getClass($morphName)], 135 - array_keys(UserReport::ALLOWED_REASONS), 136 - ); 179 + $reportables = []; 180 + 181 + foreach (MorphMap::MAP as $class => $_name) { 182 + if (isset(class_implements($class)[ReportableInterface::class])) { 183 + $reportables[] = [$class]; 184 + } 185 + } 186 + 187 + // Sanity check to make sure there are models to test. 188 + if (count($reportables) === 0) { 189 + throw new Exception('No reportables found'); 190 + } 191 + 192 + return $reportables; 137 193 } 138 194 }