From 1ba37254736bd6899823aa4f1effb527d824a5da Mon Sep 17 00:00:00 2001 From: Olivier Nicole Date: Wed, 25 Oct 2017 15:38:37 +0200 Subject: [PATCH 01/92] Complete Esperanto translation (#5520) --- app/javascript/mastodon/locales/eo.json | 336 ++++++++++++------------ 1 file changed, 168 insertions(+), 168 deletions(-) diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 22639f6f9..3f67a8fff 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -1,221 +1,221 @@ { "account.block": "Bloki @{name}", - "account.block_domain": "Hide everything from {domain}", - "account.disclaimer_full": "Information below may reflect the user's profile incompletely.", + "account.block_domain": "Kaŝi ĉion el {domain}", + "account.disclaimer_full": "La ĉi-subaj informoj povas ne plene reflekti la profilon de la uzanto.", "account.edit_profile": "Redakti la profilon", "account.follow": "Sekvi", "account.followers": "Sekvantoj", "account.follows": "Sekvatoj", "account.follows_you": "Sekvas vin", - "account.media": "Media", + "account.media": "Sonbildaĵoj", "account.mention": "Mencii @{name}", - "account.mute": "Mute @{name}", + "account.mute": "Silentigi @{name}", "account.posts": "Mesaĝoj", - "account.report": "Report @{name}", + "account.report": "Signali @{name}", "account.requested": "Atendas aprobon", - "account.share": "Share @{name}'s profile", + "account.share": "Diskonigi la profilon de @{name}", "account.unblock": "Malbloki @{name}", - "account.unblock_domain": "Unhide {domain}", - "account.unfollow": "Malsekvi", - "account.unmute": "Unmute @{name}", - "account.view_full_profile": "View full profile", - "boost_modal.combo": "You can press {combo} to skip this next time", - "bundle_column_error.body": "Something went wrong while loading this component.", - "bundle_column_error.retry": "Try again", - "bundle_column_error.title": "Network error", - "bundle_modal_error.close": "Close", - "bundle_modal_error.message": "Something went wrong while loading this component.", - "bundle_modal_error.retry": "Try again", - "column.blocks": "Blocked users", + "account.unblock_domain": "Malkaŝi {domain}", + "account.unfollow": "Ne plus sekvi", + "account.unmute": "Malsilentigi @{name}", + "account.view_full_profile": "Vidi plenan profilon", + "boost_modal.combo": "La proksiman fojon, premu {combo} por pasigi", + "bundle_column_error.body": "Io malfunkciis ŝargante tiun ĉi komponanton.", + "bundle_column_error.retry": "Bonvolu reprovi", + "bundle_column_error.title": "Reta eraro", + "bundle_modal_error.close": "Fermi", + "bundle_modal_error.message": "Io malfunkciis ŝargante tiun ĉi komponanton.", + "bundle_modal_error.retry": "Bonvolu reprovi", + "column.blocks": "Blokitaj uzantoj", "column.community": "Loka tempolinio", - "column.favourites": "Favourites", - "column.follow_requests": "Follow requests", + "column.favourites": "Favoritoj", + "column.follow_requests": "Abonpetoj", "column.home": "Hejmo", - "column.mutes": "Muted users", + "column.mutes": "Silentigitaj uzantoj", "column.notifications": "Sciigoj", - "column.pins": "Pinned toot", + "column.pins": "Alpinglitaj pepoj", "column.public": "Fratara tempolinio", "column_back_button.label": "Reveni", - "column_header.hide_settings": "Hide settings", - "column_header.moveLeft_settings": "Move column to the left", - "column_header.moveRight_settings": "Move column to the right", - "column_header.pin": "Pin", - "column_header.show_settings": "Show settings", - "column_header.unpin": "Unpin", - "column_subheading.navigation": "Navigation", - "column_subheading.settings": "Settings", - "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", - "compose_form.lock_disclaimer.lock": "locked", + "column_header.hide_settings": "Kaŝi agordojn", + "column_header.moveLeft_settings": "Movi kolumnon maldekstren", + "column_header.moveRight_settings": "Movi kolumnon dekstren", + "column_header.pin": "Alpingli", + "column_header.show_settings": "Malkaŝi agordojn", + "column_header.unpin": "Depingli", + "column_subheading.navigation": "Navigado", + "column_subheading.settings": "Agordoj", + "compose_form.lock_disclaimer": "Via konta ne estas ŝlosita. Iu ajn povas sekvi vin por vidi viajn privatajn pepojn.", + "compose_form.lock_disclaimer.lock": "ŝlosita", "compose_form.placeholder": "Pri kio vi pensas?", "compose_form.publish": "Hup", "compose_form.publish_loud": "{publish}!", "compose_form.sensitive": "Marki ke la enhavo estas tikla", "compose_form.spoiler": "Kaŝi la tekston malantaŭ averto", - "compose_form.spoiler_placeholder": "Content warning", - "confirmation_modal.cancel": "Cancel", - "confirmations.block.confirm": "Block", - "confirmations.block.message": "Are you sure you want to block {name}?", - "confirmations.delete.confirm": "Delete", - "confirmations.delete.message": "Are you sure you want to delete this status?", - "confirmations.domain_block.confirm": "Hide entire domain", - "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", - "confirmations.mute.confirm": "Mute", - "confirmations.mute.message": "Are you sure you want to mute {name}?", - "confirmations.unfollow.confirm": "Unfollow", - "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", - "embed.instructions": "Embed this status on your website by copying the code below.", - "embed.preview": "Here is what it will look like:", - "emoji_button.activity": "Activity", - "emoji_button.custom": "Custom", - "emoji_button.flags": "Flags", - "emoji_button.food": "Food & Drink", - "emoji_button.label": "Insert emoji", - "emoji_button.nature": "Nature", - "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", - "emoji_button.objects": "Objects", - "emoji_button.people": "People", - "emoji_button.recent": "Frequently used", - "emoji_button.search": "Search...", - "emoji_button.search_results": "Search results", - "emoji_button.symbols": "Symbols", - "emoji_button.travel": "Travel & Places", - "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", - "empty_column.hashtag": "There is nothing in this hashtag yet.", - "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", - "empty_column.home.public_timeline": "the public timeline", - "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", - "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", - "follow_request.authorize": "Authorize", - "follow_request.reject": "Reject", - "getting_started.appsshort": "Apps", - "getting_started.faq": "FAQ", + "compose_form.spoiler_placeholder": "Skribu tie vian averton", + "confirmation_modal.cancel": "Malfari", + "confirmations.block.confirm": "Bloki", + "confirmations.block.message": "Ĉu vi konfirmas la blokadon de {name}?", + "confirmations.delete.confirm": "Malaperigi", + "confirmations.delete.message": "Ĉu vi konfirmas la malaperigon de tiun pepon?", + "confirmations.domain_block.confirm": "Kaŝi la tutan reton", + "confirmations.domain_block.message": "Ĉu vi vere, vere certas, ke vi volas bloki {domain} tute? Plej ofte, kelkaj celitaj blokadoj aŭ silentigoj estas sufiĉaj kaj preferindaj.", + "confirmations.mute.confirm": "Silentigi", + "confirmations.mute.message": "Ĉu vi konfirmas la silentigon de {name}?", + "confirmations.unfollow.confirm": "Ne plu sekvi", + "confirmations.unfollow.message": "Ĉu vi volas ĉesi sekvi {name}?", + "embed.instructions": "Enmetu tiun statkonigon ĉe vian retejon kopiante la ĉi-suban kodon.", + "embed.preview": "Ĝi aperos tiel:", + "emoji_button.activity": "Aktivecoj", + "emoji_button.custom": "Personaj", + "emoji_button.flags": "Flagoj", + "emoji_button.food": "Manĝi kaj trinki", + "emoji_button.label": "Enmeti mieneton", + "emoji_button.nature": "Naturo", + "emoji_button.not_found": "Neniuj mienetoj!! (╯°□°)╯︵ ┻━┻", + "emoji_button.objects": "Objektoj", + "emoji_button.people": "Homoj", + "emoji_button.recent": "Ofte uzataj", + "emoji_button.search": "Serĉo…", + "emoji_button.search_results": "Rezultatoj de serĉo", + "emoji_button.symbols": "Simboloj", + "emoji_button.travel": "Vojaĝoj & lokoj", + "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!", + "empty_column.hashtag": "Ĝise, neniu enhavo estas asociita kun tiu kradvorto.", + "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.", + "empty_column.home.public_timeline": "la publika tempolinio", + "empty_column.notifications": "Vi dume ne havas sciigojn. Interagi kun aliajn uzantojn por komenci la konversacion.", + "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj instancoj por plenigi la publikan tempolinion.", + "follow_request.authorize": "Akcepti", + "follow_request.reject": "Rifuzi", + "getting_started.appsshort": "Aplikaĵoj", + "getting_started.faq": "Oftaj demandoj", "getting_started.heading": "Por komenci", - "getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}.", - "getting_started.userguide": "User Guide", - "home.column_settings.advanced": "Advanced", - "home.column_settings.basic": "Basic", - "home.column_settings.filter_regex": "Filter out by regular expressions", - "home.column_settings.show_reblogs": "Show boosts", - "home.column_settings.show_replies": "Show replies", - "home.settings": "Column settings", + "getting_started.open_source_notice": "Mastodono estas malfermkoda programo. Vi povas kontribui aŭ raporti problemojn en GitHub je {github}.", + "getting_started.userguide": "Gvidilo de uzo", + "home.column_settings.advanced": "Precizaj agordoj", + "home.column_settings.basic": "Bazaj agordoj", + "home.column_settings.filter_regex": "Forfiltri per regulesprimo", + "home.column_settings.show_reblogs": "Montri diskonigojn", + "home.column_settings.show_replies": "Montri respondojn", + "home.settings": "Agordoj de la kolumno", "lightbox.close": "Fermi", - "lightbox.next": "Next", - "lightbox.previous": "Previous", - "loading_indicator.label": "Ŝarĝanta...", - "media_gallery.toggle_visible": "Toggle visibility", - "missing_indicator.label": "Not found", - "navigation_bar.blocks": "Blocked users", + "lightbox.next": "Malantaŭa", + "lightbox.previous": "Antaŭa", + "loading_indicator.label": "Ŝarganta…", + "media_gallery.toggle_visible": "Baskuli videblecon", + "missing_indicator.label": "Ne trovita", + "navigation_bar.blocks": "Blokitaj uzantoj", "navigation_bar.community_timeline": "Loka tempolinio", "navigation_bar.edit_profile": "Redakti la profilon", - "navigation_bar.favourites": "Favourites", - "navigation_bar.follow_requests": "Follow requests", - "navigation_bar.info": "Extended information", + "navigation_bar.favourites": "Favoritaj", + "navigation_bar.follow_requests": "Abonpetoj", + "navigation_bar.info": "Plia informo", "navigation_bar.logout": "Elsaluti", - "navigation_bar.mutes": "Muted users", - "navigation_bar.pins": "Pinned toots", + "navigation_bar.mutes": "Silentigitaj uzantoj", + "navigation_bar.pins": "Alpinglitaj pepoj", "navigation_bar.preferences": "Preferoj", "navigation_bar.public_timeline": "Fratara tempolinio", "notification.favourite": "{name} favoris vian mesaĝon", "notification.follow": "{name} sekvis vin", "notification.mention": "{name} menciis vin", "notification.reblog": "{name} diskonigis vian mesaĝon", - "notifications.clear": "Clear notifications", - "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.clear": "Forviŝi la sciigojn", + "notifications.clear_confirmation": "Ĉu vi certe volas malaperigi ĉiujn viajn sciigojn?", "notifications.column_settings.alert": "Retumilaj atentigoj", - "notifications.column_settings.favourite": "Favoroj:", + "notifications.column_settings.favourite": "Favoritoj:", "notifications.column_settings.follow": "Novaj sekvantoj:", "notifications.column_settings.mention": "Mencioj:", - "notifications.column_settings.push": "Push notifications", - "notifications.column_settings.push_meta": "This device", + "notifications.column_settings.push": "Puŝsciigoj", + "notifications.column_settings.push_meta": "Tiu ĉi aparato", "notifications.column_settings.reblog": "Diskonigoj:", "notifications.column_settings.show": "Montri en kolono", - "notifications.column_settings.sound": "Play sound", - "onboarding.done": "Done", - "onboarding.next": "Next", - "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.", - "onboarding.page_four.home": "The home timeline shows posts from people you follow.", - "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", - "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", - "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}", - "onboarding.page_one.welcome": "Welcome to Mastodon!", - "onboarding.page_six.admin": "Your instance's admin is {admin}.", - "onboarding.page_six.almost_done": "Almost done...", - "onboarding.page_six.appetoot": "Bon Appetoot!", - "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.", - "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.", - "onboarding.page_six.guidelines": "community guidelines", - "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!", - "onboarding.page_six.various_app": "mobile apps", - "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.", - "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.", - "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.", - "onboarding.skip": "Skip", - "privacy.change": "Adjust status privacy", - "privacy.direct.long": "Post to mentioned users only", - "privacy.direct.short": "Direct", - "privacy.private.long": "Post to followers only", - "privacy.private.short": "Followers-only", - "privacy.public.long": "Post to public timelines", - "privacy.public.short": "Public", - "privacy.unlisted.long": "Do not show in public timelines", - "privacy.unlisted.short": "Unlisted", - "relative_time.days": "{number}d", + "notifications.column_settings.sound": "Eligi sonon", + "onboarding.done": "Farita", + "onboarding.next": "Malantaŭa", + "onboarding.page_five.public_timelines": "La loka tempolinio enhavas mesaĝojn de ĉiuj ĉe {domain}. La federacia tempolinio enhavas ĉiujn mesaĝojn de uzantoj, kiujn iu ĉe {domain} sekvas. Ambaŭ tre utilas por trovi novajn kunparolantojn.", + "onboarding.page_four.home": "La hejma tempolinio enhavas la mesaĝojn de ĉiuj uzantoj, kiuj vi sekvas.", + "onboarding.page_four.notifications": "La sciiga kolumno informas vin kiam iu interagas kun vi.", + "onboarding.page_one.federation": "Mastodono estas reto de nedependaj serviloj, unuiĝintaj por krei pligrandan socian retejon. Ni nomas tiujn servilojn instancoj.", + "onboarding.page_one.handle": "Vi estas ĉe {domain}, unu el la multaj instancoj de Mastodono. Via kompleta uznomo do estas {handle}", + "onboarding.page_one.welcome": "Bonvenon al Mastodono!", + "onboarding.page_six.admin": "Via instancestro estas {admin}.", + "onboarding.page_six.almost_done": "Estas preskaŭ finita…", + "onboarding.page_six.appetoot": "Bonan a‘pepi’ton!", + "onboarding.page_six.apps_available": "{apps} estas elŝuteblaj por iOS, Androido kaj alioj. Kaj nun… bonan a‘pepi’ton!", + "onboarding.page_six.github": "Mastodono estas libera, senpaga kaj malfermkoda programaro. Vi povas signali cimojn, proponi funkciojn aŭ kontribui al gîa kreskado ĉe {github}.", + "onboarding.page_six.guidelines": "komunreguloj", + "onboarding.page_six.read_guidelines": "Ni petas vin: ne forgesu legi la {guidelines}n de {domain}!", + "onboarding.page_six.various_app": "telefon-aplikaĵoj", + "onboarding.page_three.profile": "Redaktu vian profilon por ŝanĝi vian avataron, priskribon kaj vian nomon. Vi tie trovos ankoraŭ aliajn agordojn.", + "onboarding.page_three.search": "Uzu la serĉokampo por trovi uzantojn kaj esplori kradvortojn tiel ke {illustration} kaj {introductions}. Por trovi iun, kiu ne estas ĉe ĉi tiu instanco, uzu ĝian kompletan uznomon.", + "onboarding.page_two.compose": "Skribu pepojn en la verkkolumno. Vi povas aldoni bildojn, ŝanĝi la agordojn de privateco kaj aldoni tiklavertojn (« content warning ») dank' al la piktogramoj malsupre.", + "onboarding.skip": "Pasigi", + "privacy.change": "Alĝustigi la privateco de la mesaĝo", + "privacy.direct.long": "Vidigi nur al la menciitaj personoj", + "privacy.direct.short": "Rekta", + "privacy.private.long": "Vidigi nur al viaj sekvantoj", + "privacy.private.short": "Nursekvanta", + "privacy.public.long": "Vidigi en publikaj tempolinioj", + "privacy.public.short": "Publika", + "privacy.unlisted.long": "Ne vidigi en publikaj tempolinioj", + "privacy.unlisted.short": "Nelistigita", + "relative_time.days": "{number}t", "relative_time.hours": "{number}h", - "relative_time.just_now": "now", + "relative_time.just_now": "nun", "relative_time.minutes": "{number}m", "relative_time.seconds": "{number}s", - "reply_indicator.cancel": "Rezigni", - "report.placeholder": "Additional comments", - "report.submit": "Submit", - "report.target": "Reporting", + "reply_indicator.cancel": "Malfari", + "report.placeholder": "Pliaj komentoj", + "report.submit": "Sendi", + "report.target": "Signalaĵo", "search.placeholder": "Serĉi", - "search_popout.search_format": "Advanced search format", - "search_popout.tips.hashtag": "hashtag", - "search_popout.tips.status": "status", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", - "search_popout.tips.user": "user", - "search_results.total": "{count, number} {count, plural, one {result} other {results}}", - "standalone.public_title": "A look inside...", - "status.cannot_reblog": "This post cannot be boosted", + "search_popout.search_format": "Detala serĉo", + "search_popout.tips.hashtag": "kradvorto", + "search_popout.tips.status": "statkonigo", + "search_popout.tips.text": "Simpla teksto eligas la kongruajn afiŝnomojn, uznomojn kaj kradvortojn.", + "search_popout.tips.user": "uzanto", + "search_results.total": "{count, number} {count, plural, one {rezultato} other {rezultatoj}}", + "standalone.public_title": "Rigardeti…", + "status.cannot_reblog": "Tiun publikaĵon oni ne povas diskonigi", "status.delete": "Forigi", - "status.embed": "Embed", + "status.embed": "Enmeti", "status.favourite": "Favori", - "status.load_more": "Load more", - "status.media_hidden": "Media hidden", + "status.load_more": "Ŝargi plie", + "status.media_hidden": "Sonbildaĵo kaŝita", "status.mention": "Mencii @{name}", - "status.more": "More", - "status.mute_conversation": "Mute conversation", - "status.open": "Expand this status", - "status.pin": "Pin on profile", + "status.more": "Pli", + "status.mute_conversation": "Silentigi konversacion", + "status.open": "Disfaldi statkonigon", + "status.pin": "Pingli al la profilo", "status.reblog": "Diskonigi", - "status.reblogged_by": "{name} diskonigita", + "status.reblogged_by": "{name} diskonigis", "status.reply": "Respondi", - "status.replyAll": "Reply to thread", - "status.report": "Report @{name}", + "status.replyAll": "Respondi al la fadeno", + "status.report": "Signali @{name}", "status.sensitive_toggle": "Alklaki por vidi", "status.sensitive_warning": "Tikla enhavo", - "status.share": "Share", - "status.show_less": "Show less", - "status.show_more": "Show more", - "status.unmute_conversation": "Unmute conversation", - "status.unpin": "Unpin from profile", + "status.share": "Diskonigi", + "status.show_less": "Refaldi", + "status.show_more": "Disfaldi", + "status.unmute_conversation": "Malsilentigi konversacion", + "status.unpin": "Depingli de profilo", "tabs_bar.compose": "Ekskribi", - "tabs_bar.federated_timeline": "Federated", + "tabs_bar.federated_timeline": "Federacia tempolinio", "tabs_bar.home": "Hejmo", - "tabs_bar.local_timeline": "Local", + "tabs_bar.local_timeline": "Loka tempolinio", "tabs_bar.notifications": "Sciigoj", - "upload_area.title": "Drag & drop to upload", - "upload_button.label": "Aldoni enhavaĵon", - "upload_form.description": "Describe for the visually impaired", + "upload_area.title": "Algliti por alŝuti", + "upload_button.label": "Aldoni sonbildaĵon", + "upload_form.description": "Priskribi por la misvidantaj", "upload_form.undo": "Malfari", - "upload_progress.label": "Uploading...", - "video.close": "Close video", - "video.exit_fullscreen": "Exit full screen", - "video.expand": "Expand video", - "video.fullscreen": "Full screen", - "video.hide": "Hide video", - "video.mute": "Mute sound", - "video.pause": "Pause", - "video.play": "Play", - "video.unmute": "Unmute sound" + "upload_progress.label": "Alŝutanta…", + "video.close": "Fermi videon", + "video.exit_fullscreen": "Eliri el plenekrano", + "video.expand": "Vastigi videon", + "video.fullscreen": "Igi plenekrane", + "video.hide": "Kaŝi videon", + "video.mute": "Silentigi", + "video.pause": "Paŭzi", + "video.play": "Legi", + "video.unmute": "Malsilentigi" } From 74777599cfde8733cf98aefdc84200a5da381dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20e=20s=C3=B3?= Date: Wed, 25 Oct 2017 12:11:03 -0200 Subject: [PATCH 02/92] l10n: PT-BR translation updated (#5530) --- config/locales/simple_form.pt-BR.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 22cae5271..9d60e0171 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -4,6 +4,7 @@ pt-BR: hints: defaults: avatar: PNG, GIF or JPG. Arquivos de até 2MB. Eles serão diminuídos para 120x120px + digest: Enviado após um longo período de inatividade com um resumo das menções que você recebeu em sua ausência. display_name: one: 1 caracter restante other: %{count} caracteres restantes @@ -13,6 +14,7 @@ pt-BR: one: 1 caracter restante other: %{count} caracteres restantes setting_noindex: Afeta seu perfil público e as páginas de suas postagens + setting_theme: Afeta a aparência do Mastodon quando em sua conta em qualquer aparelho. imports: data: Arquivo CSV exportado de outra instância do Mastodon sessions: @@ -42,7 +44,9 @@ pt-BR: setting_default_sensitive: Sempre marcar mídia como sensível setting_delete_modal: Mostrar diálogo de confirmação antes de deletar uma postagem setting_noindex: Não quero ser indexado por mecanismos de busca + setting_reduce_motion: Reduz movimento em animações setting_system_font_ui: Usar a fonte padrão de seu sistema + setting_theme: Tema do site setting_unfollow_modal: Mostrar diálogo de confirmação antes de deixar de seguir alguém severity: Gravidade type: Tipo de importação From 20fee786b10305bfc4629d02519a2c904ebb524a Mon Sep 17 00:00:00 2001 From: Ratmir Karabut Date: Wed, 25 Oct 2017 18:21:58 +0300 Subject: [PATCH 03/92] Update Russian translation (#5517) * Add Russian translation (ru) * Fix a missing comma * Fix the wording for better consistency * Update Russian translation * Arrange Russian setting alphabetically * Fix syntax error * Update Russian translation * Fix formatting error * Update Russian translation * Update Russian translation * Update ru.jsx * Fix syntax error * Remove two_factor_auth.warning (appears obsolete) * Add missing strings in ru.yml A lot of new strings translated, especially for the newly added admin section * Fix translation consistency * Update Russian translation * Update Russian translation (pluralizations) * Update Russian translation * Update Russian translation * Update Russian translation (pin) * Update Russian translation (account deletion) * Fix extra line * Update Russian translation (sessions) * Update Russian translation * Update Russian translation * Fix merge conflicts (revert) * Update Russian translation * Update Russian translation (fix) * Update Russian translation (fix quotes) * Update Russian translation (fix quotes) * Update Russian translation (fix) * Update Russian translation * Add quotes * bundle exec i18n-tasks normalize --- app/javascript/mastodon/locales/ru.json | 64 ++++----- config/locales/ru.yml | 181 +++++++++++++++++++++++- config/locales/simple_form.ru.yml | 4 + 3 files changed, 210 insertions(+), 39 deletions(-) diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 104b063f5..65bc5b374 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -63,20 +63,20 @@ "confirmations.mute.message": "Вы уверены, что хотите заглушить {name}?", "confirmations.unfollow.confirm": "Отписаться", "confirmations.unfollow.message": "Вы уверены, что хотите отписаться от {name}?", - "embed.instructions": "Embed this status on your website by copying the code below.", - "embed.preview": "Here is what it will look like:", + "embed.instructions": "Встройте этот статус на Вашем сайте, скопировав код внизу.", + "embed.preview": "Так это будет выглядеть:", "emoji_button.activity": "Занятия", - "emoji_button.custom": "Custom", + "emoji_button.custom": "Собственные", "emoji_button.flags": "Флаги", "emoji_button.food": "Еда и напитки", "emoji_button.label": "Вставить эмодзи", "emoji_button.nature": "Природа", - "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", + "emoji_button.not_found": "Нет эмодзи!! (╯°□°)╯︵ ┻━┻", "emoji_button.objects": "Предметы", "emoji_button.people": "Люди", - "emoji_button.recent": "Frequently used", + "emoji_button.recent": "Последние", "emoji_button.search": "Найти...", - "emoji_button.search_results": "Search results", + "emoji_button.search_results": "Результаты поиска", "emoji_button.symbols": "Символы", "emoji_button.travel": "Путешествия", "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!", @@ -159,34 +159,34 @@ "privacy.public.short": "Публичный", "privacy.unlisted.long": "Не показывать в лентах", "privacy.unlisted.short": "Скрытый", - "relative_time.days": "{number}d", - "relative_time.hours": "{number}h", - "relative_time.just_now": "now", - "relative_time.minutes": "{number}m", - "relative_time.seconds": "{number}s", + "relative_time.days": "{number}д", + "relative_time.hours": "{number}ч", + "relative_time.just_now": "только что", + "relative_time.minutes": "{number}м", + "relative_time.seconds": "{number}с", "reply_indicator.cancel": "Отмена", "report.placeholder": "Комментарий", "report.submit": "Отправить", "report.target": "Жалуемся на", "search.placeholder": "Поиск", - "search_popout.search_format": "Advanced search format", - "search_popout.tips.hashtag": "hashtag", - "search_popout.tips.status": "status", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", - "search_popout.tips.user": "user", + "search_popout.search_format": "Продвинутый формат поиска", + "search_popout.tips.hashtag": "хэштег", + "search_popout.tips.status": "статус", + "search_popout.tips.text": "Простой ввод текста покажет совпадающие имена пользователей, отображаемые имена и хэштеги", + "search_popout.tips.user": "пользователь", "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}", - "standalone.public_title": "A look inside...", + "standalone.public_title": "Прямо сейчас", "status.cannot_reblog": "Этот статус не может быть продвинут", "status.delete": "Удалить", - "status.embed": "Embed", + "status.embed": "Встроить", "status.favourite": "Нравится", "status.load_more": "Показать еще", "status.media_hidden": "Медиаконтент скрыт", "status.mention": "Упомянуть @{name}", - "status.more": "More", + "status.more": "Больше", "status.mute_conversation": "Заглушить тред", "status.open": "Развернуть статус", - "status.pin": "Pin on profile", + "status.pin": "Закрепить в профиле", "status.reblog": "Продвинуть", "status.reblogged_by": "{name} продвинул(а)", "status.reply": "Ответить", @@ -194,11 +194,11 @@ "status.report": "Пожаловаться", "status.sensitive_toggle": "Нажмите для просмотра", "status.sensitive_warning": "Чувствительный контент", - "status.share": "Share", + "status.share": "Поделиться", "status.show_less": "Свернуть", "status.show_more": "Развернуть", "status.unmute_conversation": "Снять глушение с треда", - "status.unpin": "Unpin from profile", + "status.unpin": "Открепить от профиля", "tabs_bar.compose": "Написать", "tabs_bar.federated_timeline": "Глобальная", "tabs_bar.home": "Главная", @@ -206,16 +206,16 @@ "tabs_bar.notifications": "Уведомления", "upload_area.title": "Перетащите сюда, чтобы загрузить", "upload_button.label": "Добавить медиаконтент", - "upload_form.description": "Describe for the visually impaired", + "upload_form.description": "Описать для людей с нарушениями зрения", "upload_form.undo": "Отменить", "upload_progress.label": "Загрузка...", - "video.close": "Close video", - "video.exit_fullscreen": "Exit full screen", - "video.expand": "Expand video", - "video.fullscreen": "Full screen", - "video.hide": "Hide video", - "video.mute": "Mute sound", - "video.pause": "Pause", - "video.play": "Play", - "video.unmute": "Unmute sound" + "video.close": "Закрыть видео", + "video.exit_fullscreen": "Покинуть полноэкранный режим", + "video.expand": "Развернуть видео", + "video.fullscreen": "Полноэкранный режим", + "video.hide": "Скрыть видео", + "video.mute": "Заглушить звук", + "video.pause": "Пауза", + "video.play": "Пуск", + "video.unmute": "Включить звук" } diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 9ca08831e..7c9caec14 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1,39 +1,77 @@ --- ru: about: + about_hashtag_html: Это публичные статусы, отмеченные хэштегом #%{hashtag}. Вы можете взаимодействовать с ними при наличии у Вас аккаунта в глобальной сети Mastodon. about_mastodon_html: Mastodon - это свободная социальная сеть с открытым исходным кодом. Как децентрализованная альтернатива коммерческим платформам, Mastodon предотвращает риск монополизации Вашего общения одной компанией. Выберите сервер, которому Вы доверяете — что бы Вы ни выбрали, Вы сможете общаться со всеми остальными. Любой может запустить свой собственный узел Mastodon и участвовать в социальной сети совершенно бесшовно. about_this: Об этом узле closed_registrations: В данный момент регистрация на этом узле закрыта. contact: Связаться + contact_missing: Не установлено + contact_unavailable: Недоступен description_headline: Что такое %{domain}? domain_count_after: другими узлами domain_count_before: Связан с + extended_description_html: | +

Хорошее место для правил

+

Расширенное описание еще не настроено.

+ features: + humane_approach_body: Наученный ошибками других проектов, Mastodon направлен на выбор этичных решений в борьбе со злоупотреблениями возможностями социальных сетей. + humane_approach_title: Человечный подход + not_a_product_body: Mastodon - не коммерческая сеть. Здесь нет рекламы, сбора данных, отгороженных мест. Здесь нет централизованного управления. + not_a_product_title: Вы - человек, а не продукт + real_conversation_body: С 500 символами в Вашем распоряжении и поддержкой предупреждений о содержании статусов Вы сможете выражать свои мысли так, как Вы этого хотите. + real_conversation_title: Создан для настоящего общения + within_reach_body: Различные приложения для iOS, Android и других платформ, написанные благодаря дружественной к разработчикам экосистеме API, позволят Вам держать связь с Вашими друзьями где угодно. + within_reach_title: Всегда под рукой + find_another_instance: Найти другой узел + generic_description: "%{domain} - один из серверов сети" + hosted_on: Mastodon размещен на %{domain} + learn_more: Узнать больше other_instances: Другие узлы source_code: Исходный код status_count_after: статусов status_count_before: Опубликовано user_count_after: пользователей user_count_before: Здесь живет + what_is_mastodon: Что такое Mastodon? accounts: follow: Подписаться followers: Подписчики following: Подписан(а) + media: Медиаконтент nothing_here: Здесь ничего нет! people_followed_by: Люди, на которых подписан(а) %{name} people_who_follow: Подписчики %{name} posts: Посты + posts_with_replies: Посты с ответами remote_follow: Подписаться на удаленном узле + reserved_username: Имя пользователя зарезервировано + roles: + admin: Администратор unfollow: Отписаться admin: + account_moderation_notes: + account: Модератор + create: Создать + created_at: Дата + created_msg: Заметка модератора успешно создана! + delete: Удалить + destroyed_msg: Заметка модератора успешно удалена! accounts: are_you_sure: Вы уверены? + confirm: Подтвердить + confirmed: Подтверждено + disable_two_factor_authentication: Отключить 2FA display_name: Отображаемое имя domain: Домен edit: Изменить email: E-mail feed_url: URL фида followers: Подписчики + followers_url: URL подписчиков follows: Подписки + inbox_url: URL входящих + ip: IP location: all: Все local: Локальные @@ -45,6 +83,7 @@ ru: silenced: Заглушенные suspended: Заблокированные title: Модерация + moderation_notes: Заметки модератора most_recent_activity: Последняя активность most_recent_ip: Последний IP not_subscribed: Не подписаны @@ -52,19 +91,51 @@ ru: alphabetic: По алфавиту most_recent: По дате title: Порядок + outbox_url: URL исходящих perform_full_suspension: Полная блокировка profile_url: URL профиля + protocol: Протокол public: Публичный push_subscription_expires: Подписка PuSH истекает + redownload: Обновить аватар + reset: Сбросить reset_password: Сбросить пароль + resubscribe: Переподписаться salmon_url: Salmon URL + search: Поиск + shared_inbox_url: URL общих входящих + show: + created_reports: Жалобы, отправленные этим аккаунтом + report: жалоба + targeted_reports: Жалобы на этот аккаунт silence: Глушение statuses: Статусы + subscribe: Подписаться title: Аккаунты undo_silenced: Снять глушение undo_suspension: Снять блокировку + unsubscribe: Отписаться username: Имя пользователя web: WWW + custom_emojis: + copied_msg: Локальная копия эмодзи успешно создана + copy: Скопироват + copy_failed_msg: Не удалось создать локальную копию эмодзи + created_msg: Эмодзи успешно создано! + delete: Удалить + destroyed_msg: Эмодзи успешно удалено! + disable: Отключить + disabled_msg: Эмодзи успешно отключено + emoji: Эмодзи + enable: Включить + enabled_msg: Эмодзи успешно включено + image_hint: PNG до 50KB + new: + title: Добавить новое эмодзи + shortcode: Шорткод + shortcode_hint: Как минимум 2 символа, только алфавитно-цифровые символы и подчеркивания + title: Собственные эмодзи + upload: Загрузить domain_blocks: add_new: Добавить новую created_msg: Блокировка домена обрабатывается @@ -74,13 +145,15 @@ ru: create: Создать блокировку hint: Блокировка домена не предотвратит создание новых аккаунтов в базе данных, но ретроактивно и автоматически применит указанные методы модерации для этих аккаунтов. severity: - desc_html: "Глушение сделает статусы аккаунта невидимыми для всех, кроме их подписчиков. Блокировка удалит весь контент аккаунта, включая мультимедийные вложения и данные профиля." + desc_html: "Глушение сделает статусы аккаунта невидимыми для всех, кроме их подписчиков. Блокировка удалит весь контент аккаунта, включая мультимедийные вложения и данные профиля. Используйте Ничего, если хотите только запретить медиаконтент." + noop: Ничего silence: Глушение suspend: Блокировка title: Новая доменная блокировка reject_media: Запретить медиаконтент reject_media_hint: Удаляет локально хранимый медиаконтент и запрещает его загрузку в будущем. Не имеет значения в случае блокировки. severities: + noop: Ничего silence: Глушение suspend: Блокировка severity: Строгость @@ -97,13 +170,34 @@ ru: undo: Отменить title: Доменные блокировки undo: Отемнить + email_domain_blocks: + add_new: Добавить новую + created_msg: Доменная блокировка еmail успешно создана + delete: Удалить + destroyed_msg: Доменная блокировка еmail успешно удалена + domain: Домен + new: + create: Создать блокировку + title: Новая доменная блокировка еmail + title: Доменная блокировка email + instances: + account_count: Известных аккаунтов + domain_name: Домен + reset: Сбросить + search: Поиск + title: Известные узлы reports: + action_taken_by: 'Действие предпринято:' + are_you_sure: Вы уверены? comment: label: Комментарий none: Нет delete: Удалить id: ID mark_as_resolved: Отметить как разрешенную + nsfw: + 'false': Показать мультимедийные вложения + 'true': Скрыть мультимедийные вложения report: 'Жалоба #%{id}' reported_account: Аккаунт нарушителя reported_by: Отправитель жалобы @@ -116,6 +210,9 @@ ru: unresolved: Неразрешенные view: Просмотреть settings: + bootstrap_timeline_accounts: + desc_html: Разделяйте имена пользователей запятыми. Сработает только для локальных незакрытых аккаунтов. По умолчанию включены все локальные администраторы. + title: Подписки по умолчанию для новых пользователей contact_information: email: Введите публичный e-mail username: Введите имя пользователя @@ -123,7 +220,11 @@ ru: closed_message: desc_html: Отображается на титульной странице, когда закрыта регистрация
Можно использовать HTML-теги title: Сообщение о закрытой регистрации + deletion: + desc_html: Позволяет всем удалять собственные аккаунты + title: Разрешить удаление аккаунтов open: + desc_html: Позволяет любому создавать аккаунт title: Открыть регистрацию site_description: desc_html: Отображается в качестве параграфа на титульной странице и используется в качестве мета-тега.
Можно использовать HTML-теги, в особенности <a> и <em>. @@ -131,8 +232,32 @@ ru: site_description_extended: desc_html: Отображается на странице дополнительной информации
Можно использовать HTML-теги title: Расширенное описание сайта + site_terms: + desc_html: Вы можете добавить сюда собственную политику конфиденциальности, пользовательское соглашение и другие документы. Можно использовать теги HTML. + title: Условия использования site_title: Название сайта + thumbnail: + desc_html: Используется для предпросмотра с помощью OpenGraph и API. Рекомендуется разрешение 1200x630px + title: Картинка узла + timeline_preview: + desc_html: Показывать публичную ленту на целевой странице + title: Предпросмотр ленты title: Настройки сайта + statuses: + back_to_account: Назад к странице аккаунта + batch: + delete: Удалить + nsfw_off: Выключить NSFW + nsfw_on: Включить NSFW + execute: Выполнить + failed_to_execute: Не удалось выполнить + media: + hide: Скрыть медиаконтент + show: Показать медиаконтент + title: Медиаконтент + no_media: Без медиаконтента + title: Статусы аккаунта + with_media: С медиаконтентом subscriptions: callback_url: Callback URL confirmed: Подтверждено @@ -141,18 +266,31 @@ ru: title: WebSub topic: Тема title: Администрирование + admin_mailer: + new_report: + body: "%{reporter} подал(а) жалобу на %{target}" + subject: Новая жалоба, узел %{instance} (#%{id}) application_mailer: + salutation: "%{name}," settings: 'Изменить настройки e-mail: %{link}' signature: Уведомления Mastodon от %{instance} view: 'Просмотр:' applications: + created: Приложение успешно создано + destroyed: Приложение успешно удалено invalid_url: Введенный URL неверен + regenerate_token: Повторно сгенерировать токен доступа + token_regenerated: Токен доступа успешно сгенерирован + warning: Будьте очень внимательны с этими данными. Не делитесь ими ни с кем! + your_token: Ваш токен доступа auth: + agreement_html: Создавая аккаунт, вы соглашаетесь с нашими правилами поведения и политикой конфиденциальности. change_password: Изменить пароль delete_account: Удалить аккаунт delete_account_html: Если Вы хотите удалить свой аккаунт, вы можете перейти сюда. У Вас будет запрошено подтверждение. didnt_get_confirmation: Не получили инструкцию для подтверждения? forgot_password: Забыли пароль? + invalid_reset_password_token: Токен сброса пароля неверен или устарел. Пожалуйста, запросите новый. login: Войти logout: Выйти register: Зарегистрироваться @@ -162,6 +300,12 @@ ru: authorize_follow: error: К сожалению, при поиске удаленного аккаунта возникла ошибка follow: Подписаться + follow_request: 'Вы отправили запрос на подписку:' + following: 'Ура! Теперь Вы подписаны на:' + post_follow: + close: Или просто закрыть это окно. + return: Вернуться к профилю пользователя + web: Перейти к WWW title: Подписаться на %{acct} datetime: distance_in_words: @@ -193,7 +337,10 @@ ru: content: Проверка безопасности не удалась. Возможно, Вы блокируете cookies? title: Проверка безопасности не удалась. '429': Слишком много запросов - noscript_html: Для работы с Mastodon, пожалуйста, включите JavaScript. + '500': + content: Приносим извинения, но на нашей стороне что-то пошло не так. + title: Страница неверна + noscript_html: Для работы с Mastodon, пожалуйста, включите JavaScript. Кроме того, вы можете использовать одно из приложений Mastodon для Вашей платформы. exports: blocks: Список блокировки csv: CSV @@ -265,23 +412,30 @@ ru: number: human: decimal_units: - format: "%n%u" + format: "%n %u" units: - billion: B - million: M + billion: млрд + million: млн quadrillion: Q - thousand: K - trillion: T + thousand: тыс + trillion: трлн unit: '' pagination: next: След prev: Пред truncate: "…" + preferences: + languages: Языки + other: Другое + publishing: Публикация + web: WWW push_notifications: favourite: title: Ваш статус понравился %{name} follow: title: "%{name} теперь подписан(а) на Вас" + group: + title: "%{count} уведомлений" mention: action_boost: Продвинуть action_expand: Развернуть @@ -335,16 +489,24 @@ ru: authorized_apps: Авторизованные приложения back: Назад в Mastodon delete: Удаление аккаунта + development: Разработка edit_profile: Изменить профиль export: Экспорт данных followers: Авторизованные подписчики import: Импорт + notifications: Уведомления preferences: Настройки settings: Опции two_factor_authentication: Двухфакторная аутентификация + your_apps: Ваши приложения statuses: open_in_web: Открыть в WWW over_character_limit: превышен лимит символов (%{max}) + pin_errors: + limit: Слишком много закрепленных статусов + ownership: Нельзя закрепить чужой статус + private: Нельзя закрепить непубличный статус + reblog: Нельзя закрепить продвинутый статус show_more: Подробнее visibilities: private: Для подписчиков @@ -359,6 +521,8 @@ ru: sensitive_content: Чувствительный контент terms: title: Условия обслуживания и политика конфиденциальности %{instance} + themes: + default: Mastodon time: formats: default: "%b %d, %Y, %H:%M" @@ -367,11 +531,13 @@ ru: description_html: При включении двухфакторной аутентификации, вход потребует от Вас использования Вашего телефона, который сгенерирует входные токены. disable: Отключить enable: Включить + enabled: Двухфакторная аутентификация включена enabled_success: Двухфакторная аутентификация успешно включена generate_recovery_codes: Сгенерировать коды восстановления instructions_html: "Отсканируйте этот QR-код с помощью Google Authenticator или другого подобного приложения на Вашем телефоне. С этого момента приложение будет генерировать токены, которые будет необходимо ввести для входа." lost_recovery_codes: Коды восстановления позволяют вернуть доступ к аккаунту в случае утери телефона. Если Вы потеряли Ваши коды восстановления, вы можете заново сгенерировать их здесь. Ваши старые коды восстановления будут аннулированы. manual_instructions: 'Если Вы не можете отсканировать QR-код и хотите ввести его вручную, секрет представлен здесь открытым текстом:' + recovery_codes: Коды восстановления recovery_codes_regenerated: Коды восстановления успешно сгенерированы recovery_instructions_html: В случае утери доступа к Вашему телефону Вы можете использовать один из кодов восстановления, указанных ниже, чтобы вернуть доступ к аккаунту. Держите коды восстановления в безопасности, например, распечатав их и храня с другими важными документами. setup: Настроить @@ -379,3 +545,4 @@ ru: users: invalid_email: Введенный e-mail неверен invalid_otp_token: Введен неверный код + signed_in_as: 'Выполнен вход под именем:' diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 3bdb7870f..1b780ac26 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -4,6 +4,7 @@ ru: hints: defaults: avatar: PNG, GIF или JPG. Максимально 2MB. Будет уменьшено до 120x120px + digest: Отсылается после долгого периода неактивности с общей информацией упоминаний, полученных в Ваше отсутствие display_name: few: Осталось %{count} символа many: Осталось %{count} символов @@ -17,6 +18,7 @@ ru: one: Остался 1 символ other: Осталось %{count} символов setting_noindex: Относится к Вашему публичному профилю и страницам статусов + setting_theme: Влияет на внешний вид Mastodon при выполненном входе в аккаунт. imports: data: Файл CSV, экспортированный с другого узла Mastodon sessions: @@ -46,6 +48,8 @@ ru: setting_default_sensitive: Всегда отмечать медиаконтент как чувствительный setting_delete_modal: Показывать диалог подтверждения перед удалением setting_noindex: Отказаться от индексации в поисковых машинах + setting_reduce_motion: Уменьшить движение в анимации + setting_site_theme: Тема сайта setting_system_font_ui: Использовать шрифт системы по умолчанию setting_unfollow_modal: Показывать диалог подтверждения перед тем, как отписаться от аккаунта severity: Строгость From 02f7f3619a2af35ec29aface76bd6b5ba0f1a725 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Thu, 26 Oct 2017 06:46:50 -0700 Subject: [PATCH 04/92] Remove translateZ(0) on modal overlay (#5478) --- app/javascript/styles/mastodon/components.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index ead1c8866..d7216cd1d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3067,7 +3067,6 @@ button.icon-button.active i.fa-retweet { right: 0; bottom: 0; background: rgba($base-overlay-background, 0.7); - transform: translateZ(0); } .modal-root__container { From 4f337c020a7ca7a77086af5021c9c152fa1f07fc Mon Sep 17 00:00:00 2001 From: unarist Date: Thu, 26 Oct 2017 22:48:35 +0900 Subject: [PATCH 05/92] Fix Cocaine::ExitStatusError when upload small non-animated GIF (#5489) Looks like copied tempfile need to be flushed before further processing. This issue won't happen if the uploaded file has enough file size. --- lib/paperclip/gif_transcoder.rb | 1 + spec/fixtures/files/mini-static.gif | Bin 0 -> 1188 bytes spec/models/media_attachment_spec.rb | 31 +++++++++++++++++---------- 3 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 spec/fixtures/files/mini-static.gif diff --git a/lib/paperclip/gif_transcoder.rb b/lib/paperclip/gif_transcoder.rb index cbe53b27b..629e37581 100644 --- a/lib/paperclip/gif_transcoder.rb +++ b/lib/paperclip/gif_transcoder.rb @@ -10,6 +10,7 @@ module Paperclip unless options[:style] == :original && num_frames > 1 tmp_file = Paperclip::TempfileFactory.new.generate(attachment.instance.file_file_name) tmp_file << file.read + tmp_file.flush return tmp_file end diff --git a/spec/fixtures/files/mini-static.gif b/spec/fixtures/files/mini-static.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe597b215551713cc487b98e6dd1e820f632787b GIT binary patch literal 1188 zcmX9-ZDHmY2RGzewceuYnQBUUf0@m``YHk#f38}a!&1;N>t^uzMNlf zy0?4*0SpMB83P>P2#(q5@C48B0REkCkl!y{j5=u(R$UsJ9OeSPXmT}~T>XZFHg z*&7FNAP&qyI4B3>2#&;&ISNPRXq>=_I58*Tq@0WmY{bTF!lrDQg3LGxSRew;jEI9c z63601oQgB?5KrP+yoguvCIJ#i0!t7HD#0W|B1vS4B2gupBuFAjEJ-A(Bol)eiLsc7 zsaR$|i%kkvh(fa%>Y$F)u{u$w>P$VQ%jIfCkdQ8bpI?Fpbbi8d;-gRE?$y znn)9C5>2Yf)SyOctR`xz=WSkABU6%b#ZEUBe(vuw1icBVYfec_Ltw|#lL*zn)1Ygg`nU_PX( z?*57Gh3x*Sx`&P~{PwN>*%geeQaAtd9yyc49E^RpUITr1`e&*$8m)F02 zeD~1$=jOb8PhngdXN~Qv`01BVUp>2)<5j(dMKhM~Jg~I;(UYtCj}7jcbHn`WYWAGi zSXKS~;om-MY3khf&%5_)HQ8b`^?1a9kHYhAI|%trLU*@lY?8|-?*o%bD!UN z^ykTU_SgRV%Bda2;Z-j!XgYn@(1OD{w6Jq@{O;E3Yh_ z{BHf4H_zQSRBS)K>(>JxZ#(qSS3}FbUi{+KN9xAcd^z)*-eTjyP469~39MC~dg)kG|6lcRxA1=G>{rUi;hgjwScL(f9Ndn_BCuZ|quzTmA=5n<3o* literal 0 HcmV?d00001 diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb index 9fce5bc4f..435b4f326 100644 --- a/spec/models/media_attachment_spec.rb +++ b/spec/models/media_attachment_spec.rb @@ -20,20 +20,29 @@ RSpec.describe MediaAttachment, type: :model do end describe 'non-animated gif non-conversion' do - let(:media) { MediaAttachment.create(account: Fabricate(:account), file: attachment_fixture('attachment.gif')) } + fixtures = [ + { filename: 'attachment.gif', width: 600, height: 400, aspect: 1.5 }, + { filename: 'mini-static.gif', width: 32, height: 32, aspect: 1.0 }, + ] - it 'sets type to image' do - expect(media.type).to eq 'image' - end + fixtures.each do |fixture| + context fixture[:filename] do + let(:media) { MediaAttachment.create(account: Fabricate(:account), file: attachment_fixture(fixture[:filename])) } - it 'leaves original file as-is' do - expect(media.file_content_type).to eq 'image/gif' - end + it 'sets type to image' do + expect(media.type).to eq 'image' + end - it 'sets meta' do - expect(media.file.meta["original"]["width"]).to eq 600 - expect(media.file.meta["original"]["height"]).to eq 400 - expect(media.file.meta["original"]["aspect"]).to eq 1.5 + it 'leaves original file as-is' do + expect(media.file_content_type).to eq 'image/gif' + end + + it 'sets meta' do + expect(media.file.meta["original"]["width"]).to eq fixture[:width] + expect(media.file.meta["original"]["height"]).to eq fixture[:height] + expect(media.file.meta["original"]["aspect"]).to eq fixture[:aspect] + end + end end end From d556be2968641c265360297fd268a24119c68670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=8A=E3=82=93=E3=81=99=E3=81=8D?= <6533808+rinsuki@users.noreply.github.com> Date: Thu, 26 Oct 2017 22:52:48 +0900 Subject: [PATCH 06/92] Fix column design broken with very long title (#5493) * Fix #5314 * fix not beautiful code * fix broken design with mobile view * remove no longer needed code --- .../mastodon/components/column_header.js | 4 +++- app/javascript/styles/mastodon/components.scss | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index e4fa8fa7a..80a8fbdb3 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -137,7 +137,9 @@ export default class ColumnHeader extends React.PureComponent {

- {title} + + {title} +
{backButton} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index d7216cd1d..516f8de7d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2280,6 +2280,7 @@ button.icon-button.active i.fa-retweet { } .column-header { + display: flex; padding: 15px; font-size: 16px; background: lighten($ui-base-color, 4%); @@ -2305,12 +2306,10 @@ button.icon-button.active i.fa-retweet { } .column-header__buttons { - position: absolute; - right: 0; - top: 0; - height: 100%; - display: flex; height: 48px; + display: flex; + margin: -15px; + margin-left: 0; } .column-header__button { @@ -2378,6 +2377,14 @@ button.icon-button.active i.fa-retweet { } } +.column-header__title { + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + flex: 1; +} + .text-btn { display: inline-block; padding: 0; From 22da775a8546a0496b4f501f7cb6e321b0168f40 Mon Sep 17 00:00:00 2001 From: erin Date: Thu, 26 Oct 2017 09:44:24 -0500 Subject: [PATCH 07/92] Fix copying emojos: redirect to the page you were on (#5509) --- app/controllers/admin/custom_emojis_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 5cce5bce4..5d9144be7 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -36,7 +36,7 @@ module Admin flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg') end - redirect_to admin_custom_emojis_path(params[:page]) + redirect_to admin_custom_emojis_path(page: params[:page]) end def enable From 0129f5eada3d146c4e3550c7c82b94520a18d2ba Mon Sep 17 00:00:00 2001 From: unarist Date: Fri, 27 Oct 2017 23:10:22 +0900 Subject: [PATCH 08/92] Optimize FixReblogsInFeeds migration (#5538) We have changed how we store reblogs in the redis for bigint IDs. This process is done by 1) scan all entries in users feed, and 2) re-store reblogs by 3 write commands. However, this operation is really slow for large instances. e.g. 1hrs on friends.nico (w/ 50k users). So I have tried below tweaks. * It checked non-reblogs by `entry[0] == entry[1]`, but this condition won't work because `entry[0]` is String while `entry[1]` is Float. Changing `entry[0].to_i == entry[1]` seems work. -> about 4-20x faster (feed with less reblogs will be faster) * Write operations can be batched by pipeline -> about 6x faster * Wrap operation by Lua script and execute by EVALSHA command. This really reduces packets between Ruby and Redis. -> about 3x faster I've taken Lua script way, though doing other optimizations may be enough. --- .../20170920032311_fix_reblogs_in_feeds.rb | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/db/migrate/20170920032311_fix_reblogs_in_feeds.rb b/db/migrate/20170920032311_fix_reblogs_in_feeds.rb index c813ecd46..439c5fca0 100644 --- a/db/migrate/20170920032311_fix_reblogs_in_feeds.rb +++ b/db/migrate/20170920032311_fix_reblogs_in_feeds.rb @@ -3,48 +3,62 @@ class FixReblogsInFeeds < ActiveRecord::Migration[5.1] redis = Redis.current fm = FeedManager.instance + # Old scheme: + # Each user's feed zset had a series of score:value entries, + # where "regular" statuses had the same score and value (their + # ID). Reblogs had a score of the reblogging status' ID, and a + # value of the reblogged status' ID. + + # New scheme: + # The feed contains only entries with the same score and value. + # Reblogs result in the reblogging status being added to the + # feed, with an entry in a reblog tracking zset (where the score + # is once again set to the reblogging status' ID, and the value + # is set to the reblogged status' ID). This is safe for Redis' + # float coersion because in this reblog tracking zset, we only + # need the rebloggging status' ID to be able to stop tracking + # entries after they have gotten too far down the feed, which + # does not require an exact value. + + # This process reads all feeds and writes 3 times for each reblogs. + # So we use Lua script to avoid overhead between Ruby and Redis. + script = <<-LUA + local timeline_key = KEYS[1] + local reblog_key = KEYS[2] + + -- So, first, we iterate over the user's feed to find any reblogs. + local items = redis.call('zrange', timeline_key, 0, -1, 'withscores') + + for i = 1, #items, 2 do + local reblogged_id = items[i] + local reblogging_id = items[i + 1] + if (reblogged_id ~= reblogging_id) then + + -- The score and value don't match, so this is a reblog. + -- (note that we're transitioning from IDs < 53 bits so we + -- don't have to worry about the loss of precision) + + -- Remove the old entry + redis.call('zrem', timeline_key, reblogged_id) + + -- Add a new one for the reblogging status + redis.call('zadd', timeline_key, reblogging_id, reblogging_id) + + -- Track the fact that this was a reblog + redis.call('zadd', reblog_key, reblogging_id, reblogged_id) + end + end + LUA + script_hash = redis.script(:load, script) + # find_each is batched on the database side. User.includes(:account).find_each do |user| account = user.account - # Old scheme: - # Each user's feed zset had a series of score:value entries, - # where "regular" statuses had the same score and value (their - # ID). Reblogs had a score of the reblogging status' ID, and a - # value of the reblogged status' ID. - - # New scheme: - # The feed contains only entries with the same score and value. - # Reblogs result in the reblogging status being added to the - # feed, with an entry in a reblog tracking zset (where the score - # is once again set to the reblogging status' ID, and the value - # is set to the reblogged status' ID). This is safe for Redis' - # float coersion because in this reblog tracking zset, we only - # need the rebloggging status' ID to be able to stop tracking - # entries after they have gotten too far down the feed, which - # does not require an exact value. - - # So, first, we iterate over the user's feed to find any reblogs. timeline_key = fm.key(:home, account.id) reblog_key = fm.key(:home, account.id, 'reblogs') - redis.zrange(timeline_key, 0, -1, with_scores: true).each do |entry| - next if entry[0] == entry[1] - # The score and value don't match, so this is a reblog. - # (note that we're transitioning from IDs < 53 bits so we - # don't have to worry about the loss of precision) - - reblogged_id, reblogging_id = entry - - # Remove the old entry - redis.zrem(timeline_key, reblogged_id) - - # Add a new one for the reblogging status - redis.zadd(timeline_key, reblogging_id, reblogging_id) - - # Track the fact that this was a reblog - redis.zadd(reblog_key, reblogging_id, reblogged_id) - end + redis.evalsha(script_hash, [timeline_key, reblog_key]) end end From 0cb329f63a292598ef9ed1af6b8f8b56658e7984 Mon Sep 17 00:00:00 2001 From: puckipedia Date: Fri, 27 Oct 2017 16:10:36 +0200 Subject: [PATCH 09/92] Allow ActivityPub Note's tag and attachment to be single objects (#5534) --- app/helpers/jsonld_helper.rb | 4 ++++ app/lib/activitypub/activity/create.rb | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index c23a2e095..a3441e6f9 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -9,6 +9,10 @@ module JsonLdHelper value.is_a?(Array) ? value.first : value end + def as_array(value) + value.is_a?(Array) ? value : [value] + end + def value_or_id(value) value.is_a?(String) || value.nil? ? value : value['id'] end diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index d6e9bc1de..376684c00 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -53,9 +53,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def process_tags(status) - return unless @object['tag'].is_a?(Array) + return if @object['tag'].nil? - @object['tag'].each do |tag| + as_array(@object['tag']).each do |tag| case tag['type'] when 'Hashtag' process_hashtag tag, status @@ -103,9 +103,9 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def process_attachments(status) - return unless @object['attachment'].is_a?(Array) + return if @object['attachment'].nil? - @object['attachment'].each do |attachment| + as_array(@object['attachment']).each do |attachment| next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank? href = Addressable::URI.parse(attachment['url']).normalize.to_s From 781105293cf129c84ef0b91ec8cd27b7127cf951 Mon Sep 17 00:00:00 2001 From: nullkal Date: Fri, 27 Oct 2017 23:11:30 +0900 Subject: [PATCH 10/92] Feature: Unlisted custom emojis (#5485) --- app/controllers/admin/custom_emojis_controller.rb | 10 +++++++++- .../containers/emoji_picker_dropdown_container.js | 2 +- app/models/custom_emoji.rb | 1 + app/serializers/rest/custom_emoji_serializer.rb | 2 +- app/views/admin/custom_emojis/_custom_emoji.html.haml | 7 ++++++- config/locales/en.yml | 4 ++++ config/routes.rb | 2 +- ...1020084748_add_visible_in_picker_to_custom_emoji.rb | 7 +++++++ db/schema.rb | 3 ++- 9 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 5d9144be7..cbd7abe95 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -22,6 +22,14 @@ module Admin end end + def update + if @custom_emoji.update(resource_params) + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.updated_msg') + else + redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.update_failed_msg') + end + end + def destroy @custom_emoji.destroy redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg') @@ -56,7 +64,7 @@ module Admin end def resource_params - params.require(:custom_emoji).permit(:shortcode, :image) + params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker) end def filtered_custom_emojis diff --git a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js index 71944128c..699687c69 100644 --- a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js +++ b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js @@ -46,7 +46,7 @@ const getFrequentlyUsedEmojis = createSelector([ const getCustomEmojis = createSelector([ state => state.get('custom_emojis'), -], emojis => emojis.sort((a, b) => { +], emojis => emojis.filter(e => e.get('visible_in_picker')).sort((a, b) => { const aShort = a.get('shortcode').toLowerCase(); const bShort = b.get('shortcode').toLowerCase(); diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 65d9840d5..28b6a2b0b 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -15,6 +15,7 @@ # disabled :boolean default(FALSE), not null # uri :string # image_remote_url :string +# visible_in_picker :boolean default(TRUE), not null # class CustomEmoji < ApplicationRecord diff --git a/app/serializers/rest/custom_emoji_serializer.rb b/app/serializers/rest/custom_emoji_serializer.rb index b958e6a5d..65686a866 100644 --- a/app/serializers/rest/custom_emoji_serializer.rb +++ b/app/serializers/rest/custom_emoji_serializer.rb @@ -3,7 +3,7 @@ class REST::CustomEmojiSerializer < ActiveModel::Serializer include RoutingHelper - attributes :shortcode, :url, :static_url + attributes :shortcode, :url, :static_url, :visible_in_picker def url full_asset_url(object.image.url) diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index 1fa64908c..399d13bbd 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -9,7 +9,12 @@ - else = custom_emoji.domain %td - - unless custom_emoji.local? + - if custom_emoji.local? + - if custom_emoji.visible_in_picker + = table_link_to 'eye', t('admin.custom_emojis.listed'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: false }), method: :patch + - else + = table_link_to 'eye-slash', t('admin.custom_emojis.unlisted'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: true }), method: :patch + - else = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page]), method: :post %td - if custom_emoji.disabled? diff --git a/config/locales/en.yml b/config/locales/en.yml index 45929e97d..2d821550a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -130,11 +130,15 @@ en: enable: Enable enabled_msg: Successfully enabled that emoji image_hint: PNG up to 50KB + listed: Listed new: title: Add new custom emoji shortcode: Shortcode shortcode_hint: At least 2 characters, only alphanumeric characters and underscores title: Custom emojis + unlisted: Unlisted + update_failed_msg: Could not update that emoji + updated_msg: Emoji successfully updated! upload: Upload domain_blocks: add_new: Add new diff --git a/config/routes.rb b/config/routes.rb index 5a6351f77..bdfcdaff6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -140,7 +140,7 @@ Rails.application.routes.draw do resource :two_factor_authentication, only: [:destroy] end - resources :custom_emojis, only: [:index, :new, :create, :destroy] do + resources :custom_emojis, only: [:index, :new, :create, :update, :destroy] do member do post :copy post :enable diff --git a/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb b/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb new file mode 100644 index 000000000..60a287101 --- /dev/null +++ b/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb @@ -0,0 +1,7 @@ +class AddVisibleInPickerToCustomEmoji < ActiveRecord::Migration[5.1] + def change + safety_assured { + add_column :custom_emojis, :visible_in_picker, :boolean, default: true, null: false + } + end +end diff --git a/db/schema.rb b/db/schema.rb index f9722ccda..697a7f374 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171010025614) do +ActiveRecord::Schema.define(version: 20171020084748) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 20171010025614) do t.boolean "disabled", default: false, null: false t.string "uri" t.string "image_remote_url" + t.boolean "visible_in_picker", default: true, null: false t.index ["shortcode", "domain"], name: "index_custom_emojis_on_shortcode_and_domain", unique: true end From e4080772b511e8dd436fddc79de10f44e4d83ff6 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 27 Oct 2017 23:54:20 +0900 Subject: [PATCH 11/92] Use contenthash for ExtractTextWebpackPlugin (#5462) [hash] is not documented. --- config/webpack/shared.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/webpack/shared.js b/config/webpack/shared.js index cd642a28a..5ff267fc5 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -55,7 +55,7 @@ module.exports = { resource.request = resource.request.replace(/^history/, 'history/es'); } ), - new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'), + new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[contenthash].css' : '[name].css'), new ManifestPlugin({ publicPath: output.publicPath, writeToFileEmit: true, From 3de22a82bf1c3c0a3b593be4075cf42a3ec9291e Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Fri, 27 Oct 2017 08:04:44 -0700 Subject: [PATCH 12/92] Refactor initial state: reduce_motion and auto_play_gif (#5501) --- .../mastodon/components/media_gallery.js | 13 +++++-------- app/javascript/mastodon/components/status.js | 4 +--- .../mastodon/containers/compose_container.js | 5 ++--- app/javascript/mastodon/containers/mastodon.js | 3 ++- .../mastodon/containers/status_container.js | 1 - .../mastodon/containers/timeline_container.js | 5 ++--- .../features/account/components/header.js | 17 +++-------------- .../mastodon/features/account_gallery/index.js | 5 +---- .../status/components/detailed_status.js | 2 -- .../mastodon/features/status/index.js | 5 +---- .../features/ui/util/optional_motion.js | 9 +-------- app/javascript/mastodon/initial_state.js | 9 +++++++++ app/views/layouts/application.html.haml | 1 - 13 files changed, 27 insertions(+), 52 deletions(-) create mode 100644 app/javascript/mastodon/initial_state.js diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js index fb71d8c5c..20febdb16 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.js @@ -6,6 +6,7 @@ import IconButton from './icon_button'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { isIOS } from '../is_mobile'; import classNames from 'classnames'; +import { autoPlayGif } from '../initial_state'; const messages = defineMessages({ toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' }, @@ -23,11 +24,9 @@ class Item extends React.PureComponent { index: PropTypes.number.isRequired, size: PropTypes.number.isRequired, onClick: PropTypes.func.isRequired, - autoPlayGif: PropTypes.bool, }; static defaultProps = { - autoPlayGif: false, standalone: false, index: 0, size: 1, @@ -47,7 +46,7 @@ class Item extends React.PureComponent { } hoverToPlay () { - const { attachment, autoPlayGif } = this.props; + const { attachment } = this.props; return !autoPlayGif && attachment.get('type') === 'gifv'; } @@ -139,7 +138,7 @@ class Item extends React.PureComponent { ); } else if (attachment.get('type') === 'gifv') { - const autoPlay = !isIOS() && this.props.autoPlayGif; + const autoPlay = !isIOS() && autoPlayGif; thumbnail = (
@@ -181,11 +180,9 @@ export default class MediaGallery extends React.PureComponent { height: PropTypes.number.isRequired, onOpenMedia: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, - autoPlayGif: PropTypes.bool, }; static defaultProps = { - autoPlayGif: false, standalone: false, }; @@ -261,9 +258,9 @@ export default class MediaGallery extends React.PureComponent { const size = media.take(4).size; if (this.isStandaloneEligible()) { - children = ; + children = ; } else { - children = media.take(4).map((attachment, i) => ); + children = media.take(4).map((attachment, i) => ); } } diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 70005436b..bed354059 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -38,7 +38,6 @@ export default class Status extends ImmutablePureComponent { onHeightChange: PropTypes.func, me: PropTypes.string, boostModal: PropTypes.bool, - autoPlayGif: PropTypes.bool, muted: PropTypes.bool, hidden: PropTypes.bool, onMoveUp: PropTypes.func, @@ -56,7 +55,6 @@ export default class Status extends ImmutablePureComponent { 'account', 'me', 'boostModal', - 'autoPlayGif', 'muted', 'hidden', ] @@ -197,7 +195,7 @@ export default class Status extends ImmutablePureComponent { } else { media = ( - {Component => } + {Component => } ); } diff --git a/app/javascript/mastodon/containers/compose_container.js b/app/javascript/mastodon/containers/compose_container.js index db452d03a..5ee1d2f14 100644 --- a/app/javascript/mastodon/containers/compose_container.js +++ b/app/javascript/mastodon/containers/compose_container.js @@ -6,15 +6,14 @@ import { hydrateStore } from '../actions/store'; import { IntlProvider, addLocaleData } from 'react-intl'; import { getLocale } from '../locales'; import Compose from '../features/standalone/compose'; +import initialState from '../initial_state'; const { localeData, messages } = getLocale(); addLocaleData(localeData); const store = configureStore(); -const initialStateContainer = document.getElementById('initial-state'); -if (initialStateContainer !== null) { - const initialState = JSON.parse(initialStateContainer.textContent); +if (initialState) { store.dispatch(hydrateStore(initialState)); } diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index 56b7bda46..e1d89a5b8 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -10,12 +10,13 @@ import { hydrateStore } from '../actions/store'; import { connectUserStream } from '../actions/streaming'; import { IntlProvider, addLocaleData } from 'react-intl'; import { getLocale } from '../locales'; +import initialState from '../initial_state'; const { localeData, messages } = getLocale(); addLocaleData(localeData); export const store = configureStore(); -const hydrateAction = hydrateStore(JSON.parse(document.getElementById('initial-state').textContent)); +const hydrateAction = hydrateStore(initialState); store.dispatch(hydrateAction); export default class Mastodon extends React.PureComponent { diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index c61b7d00d..29eb5f955 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -38,7 +38,6 @@ const makeMapStateToProps = () => { me: state.getIn(['meta', 'me']), boostModal: state.getIn(['meta', 'boost_modal']), deleteModal: state.getIn(['meta', 'delete_modal']), - autoPlayGif: state.getIn(['meta', 'auto_play_gif']), }); return mapStateToProps; diff --git a/app/javascript/mastodon/containers/timeline_container.js b/app/javascript/mastodon/containers/timeline_container.js index 4be037955..e84c921ee 100644 --- a/app/javascript/mastodon/containers/timeline_container.js +++ b/app/javascript/mastodon/containers/timeline_container.js @@ -7,15 +7,14 @@ import { IntlProvider, addLocaleData } from 'react-intl'; import { getLocale } from '../locales'; import PublicTimeline from '../features/standalone/public_timeline'; import HashtagTimeline from '../features/standalone/hashtag_timeline'; +import initialState from '../initial_state'; const { localeData, messages } = getLocale(); addLocaleData(localeData); const store = configureStore(); -const initialStateContainer = document.getElementById('initial-state'); -if (initialStateContainer !== null) { - const initialState = JSON.parse(initialStateContainer.textContent); +if (initialState) { store.dispatch(hydrateStore(initialState)); } diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index 07a6c5dec..99ead014e 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -5,8 +5,8 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import IconButton from '../../../components/icon_button'; import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; -import { connect } from 'react-redux'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import { autoPlayGif } from '../../../initial_state'; const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -14,19 +14,10 @@ const messages = defineMessages({ requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, }); -const makeMapStateToProps = () => { - const mapStateToProps = state => ({ - autoPlayGif: state.getIn(['meta', 'auto_play_gif']), - }); - - return mapStateToProps; -}; - class Avatar extends ImmutablePureComponent { static propTypes = { account: ImmutablePropTypes.map.isRequired, - autoPlayGif: PropTypes.bool.isRequired, }; state = { @@ -44,7 +35,7 @@ class Avatar extends ImmutablePureComponent { } render () { - const { account, autoPlayGif } = this.props; + const { account } = this.props; const { isHovered } = this.state; return ( @@ -71,7 +62,6 @@ class Avatar extends ImmutablePureComponent { } -@connect(makeMapStateToProps) @injectIntl export default class Header extends ImmutablePureComponent { @@ -80,7 +70,6 @@ export default class Header extends ImmutablePureComponent { me: PropTypes.string.isRequired, onFollow: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, - autoPlayGif: PropTypes.bool.isRequired, }; render () { @@ -124,7 +113,7 @@ export default class Header extends ImmutablePureComponent { return (
- + @{account.get('acct')} {lockedIcon} diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js index 2a88addc4..6a5c07568 100644 --- a/app/javascript/mastodon/features/account_gallery/index.js +++ b/app/javascript/mastodon/features/account_gallery/index.js @@ -19,7 +19,6 @@ const mapStateToProps = (state, props) => ({ medias: getAccountGallery(state, props.params.accountId), isLoading: state.getIn(['timelines', `account:${props.params.accountId}:media`, 'isLoading']), hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}:media`, 'next']), - autoPlayGif: state.getIn(['meta', 'auto_play_gif']), }); @connect(mapStateToProps) @@ -31,7 +30,6 @@ export default class AccountGallery extends ImmutablePureComponent { medias: ImmutablePropTypes.list.isRequired, isLoading: PropTypes.bool, hasMore: PropTypes.bool, - autoPlayGif: PropTypes.bool, }; componentDidMount () { @@ -67,7 +65,7 @@ export default class AccountGallery extends ImmutablePureComponent { } render () { - const { medias, autoPlayGif, isLoading, hasMore } = this.props; + const { medias, isLoading, hasMore } = this.props; let loadMore = null; @@ -100,7 +98,6 @@ export default class AccountGallery extends ImmutablePureComponent { )} {loadMore} diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js index c10e2c531..81f71749b 100644 --- a/app/javascript/mastodon/features/status/components/detailed_status.js +++ b/app/javascript/mastodon/features/status/components/detailed_status.js @@ -22,7 +22,6 @@ export default class DetailedStatus extends ImmutablePureComponent { status: ImmutablePropTypes.map.isRequired, onOpenMedia: PropTypes.func.isRequired, onOpenVideo: PropTypes.func.isRequired, - autoPlayGif: PropTypes.bool, }; handleAccountClick = (e) => { @@ -70,7 +69,6 @@ export default class DetailedStatus extends ImmutablePureComponent { media={status.get('media_attachments')} height={300} onOpenMedia={this.props.onOpenMedia} - autoPlayGif={this.props.autoPlayGif} /> ); } diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 7ad3a7644..6e95fa939 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -45,7 +45,6 @@ const makeMapStateToProps = () => { me: state.getIn(['meta', 'me']), boostModal: state.getIn(['meta', 'boost_modal']), deleteModal: state.getIn(['meta', 'delete_modal']), - autoPlayGif: state.getIn(['meta', 'auto_play_gif']), }); return mapStateToProps; @@ -68,7 +67,6 @@ export default class Status extends ImmutablePureComponent { me: PropTypes.string, boostModal: PropTypes.bool, deleteModal: PropTypes.bool, - autoPlayGif: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -257,7 +255,7 @@ export default class Status extends ImmutablePureComponent { render () { let ancestors, descendants; - const { status, ancestorsIds, descendantsIds, me, autoPlayGif } = this.props; + const { status, ancestorsIds, descendantsIds, me } = this.props; if (status === null) { return ( @@ -298,7 +296,6 @@ export default class Status extends ImmutablePureComponent {
{ // This is either an object with a "val" property or it's a number return (typeof value === 'object' && value && 'val' in value) ? value.val : value; @@ -26,12 +25,6 @@ class OptionalMotion extends React.Component { const { style, defaultStyle, children } = this.props; - if (typeof reduceMotion !== 'boolean') { - // This never changes without a page reload, so we can just grab it - // once from the body classes as opposed to using Redux's connect(), - // which would unnecessarily update every state change - reduceMotion = document.body.classList.contains('reduce-motion'); - } if (reduceMotion) { Object.keys(style).forEach(key => { if (stylesToKeep.includes(key)) { diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js new file mode 100644 index 000000000..ac7315f68 --- /dev/null +++ b/app/javascript/mastodon/initial_state.js @@ -0,0 +1,9 @@ +const element = document.getElementById('initial-state'); +const initialState = element && JSON.parse(element.textContent); + +const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop]; + +export const reduceMotion = getMeta('reduce_motion'); +export const autoPlayGif = getMeta('auto_play_gif'); + +export default initialState; diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 831858bcf..ee995c987 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -27,7 +27,6 @@ = yield :header_tags - body_classes ||= @body_classes || '' - - body_classes += ' reduce-motion' if current_account&.user&.setting_reduce_motion - body_classes += ' system-font' if current_account&.user&.setting_system_font_ui %body{ class: add_rtl_body_class(body_classes) } From 37b267e2ab8e59ce1dea33f4217a61a90488aac3 Mon Sep 17 00:00:00 2001 From: David Yip Date: Fri, 27 Oct 2017 10:05:04 -0500 Subject: [PATCH 13/92] Add artist, title, and date metadata to boop.{mp3,ogg} (#5531) For boop.mp3, this commit adds both ID3v1 and ID3v2 tags. For boop.ogg, we use Vorbis metadata. In the case of boop.mp3, this also adds a cover image. Interestingly, it didn't seem to affect the size of boop.mp3 much, despite being ~8k. boop.ogg seemed to be much more affected and so no cover image was added to that version. --- public/sounds/boop.mp3 | Bin 12070 -> 12280 bytes public/sounds/boop.ogg | Bin 5164 -> 5247 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/public/sounds/boop.mp3 b/public/sounds/boop.mp3 index 02a035d91b02049949e92f4116d2a1be6c85a4f1..bf9c3c1aaff40dea76ad82e3e853d2d32382bcea 100644 GIT binary patch literal 12280 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 literal 12070 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 From ec487166db4d9d532e6090c76b65c797780fa841 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Fri, 27 Oct 2017 10:06:54 -0700 Subject: [PATCH 14/92] Directly use if not reducing motion (#5546) --- .../features/ui/util/optional_motion.js | 50 ++----------------- .../features/ui/util/reduced_motion.js | 44 ++++++++++++++++ 2 files changed, 47 insertions(+), 47 deletions(-) create mode 100644 app/javascript/mastodon/features/ui/util/reduced_motion.js diff --git a/app/javascript/mastodon/features/ui/util/optional_motion.js b/app/javascript/mastodon/features/ui/util/optional_motion.js index 82edbbe8f..df3a8b54a 100644 --- a/app/javascript/mastodon/features/ui/util/optional_motion.js +++ b/app/javascript/mastodon/features/ui/util/optional_motion.js @@ -1,49 +1,5 @@ -// Like react-motion's Motion, but checks to see if the user prefers -// reduced motion and uses a cross-fade in those cases. - -import React from 'react'; -import Motion from 'react-motion/lib/Motion'; -import PropTypes from 'prop-types'; import { reduceMotion } from '../../../initial_state'; +import ReducedMotion from './reduced_motion'; +import Motion from 'react-motion/lib/Motion'; -const stylesToKeep = ['opacity', 'backgroundOpacity']; - -const extractValue = (value) => { - // This is either an object with a "val" property or it's a number - return (typeof value === 'object' && value && 'val' in value) ? value.val : value; -}; - -class OptionalMotion extends React.Component { - - static propTypes = { - defaultStyle: PropTypes.object, - style: PropTypes.object, - children: PropTypes.func, - } - - render() { - - const { style, defaultStyle, children } = this.props; - - if (reduceMotion) { - Object.keys(style).forEach(key => { - if (stylesToKeep.includes(key)) { - return; - } - // If it's setting an x or height or scale or some other value, we need - // to preserve the end-state value without actually animating it - style[key] = defaultStyle[key] = extractValue(style[key]); - }); - } - - return ( - - {children} - - ); - } - -} - - -export default OptionalMotion; +export default reduceMotion ? ReducedMotion : Motion; diff --git a/app/javascript/mastodon/features/ui/util/reduced_motion.js b/app/javascript/mastodon/features/ui/util/reduced_motion.js new file mode 100644 index 000000000..95519042b --- /dev/null +++ b/app/javascript/mastodon/features/ui/util/reduced_motion.js @@ -0,0 +1,44 @@ +// Like react-motion's Motion, but reduces all animations to cross-fades +// for the benefit of users with motion sickness. +import React from 'react'; +import Motion from 'react-motion/lib/Motion'; +import PropTypes from 'prop-types'; + +const stylesToKeep = ['opacity', 'backgroundOpacity']; + +const extractValue = (value) => { + // This is either an object with a "val" property or it's a number + return (typeof value === 'object' && value && 'val' in value) ? value.val : value; +}; + +class ReducedMotion extends React.Component { + + static propTypes = { + defaultStyle: PropTypes.object, + style: PropTypes.object, + children: PropTypes.func, + } + + render() { + + const { style, defaultStyle, children } = this.props; + + Object.keys(style).forEach(key => { + if (stylesToKeep.includes(key)) { + return; + } + // If it's setting an x or height or scale or some other value, we need + // to preserve the end-state value without actually animating it + style[key] = defaultStyle[key] = extractValue(style[key]); + }); + + return ( + + {children} + + ); + } + +} + +export default ReducedMotion; From e843f62f479d9b8b2d177e587c3e10b5e3945f68 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Fri, 27 Oct 2017 10:08:07 -0700 Subject: [PATCH 15/92] Avoid unnecessary Motion components in icon_button.js (#5544) --- .../mastodon/components/icon_button.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js index d8e445cef..06f53841d 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.js @@ -72,6 +72,25 @@ export default class IconButton extends React.PureComponent { overlayed: overlay, }); + if (!animate) { + // Perf optimization: avoid unnecessary components unless + // we actually need to animate. + return ( +