about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--app/assets/javascripts/components/actions/compose.jsx5
-rw-r--r--app/assets/javascripts/components/containers/mastodon.jsx2
-rw-r--r--app/assets/javascripts/components/locales/bg.jsx2
-rw-r--r--app/assets/javascripts/components/locales/de.jsx4
-rw-r--r--app/assets/javascripts/components/locales/en.jsx1
-rw-r--r--app/assets/javascripts/components/locales/index.jsx2
-rw-r--r--app/assets/javascripts/components/locales/nl.jsx68
-rw-r--r--app/assets/stylesheets/stream_entries.scss1
-rw-r--r--app/helpers/settings_helper.rb1
-rw-r--r--app/validators/email_validator.rb (renamed from app/lib/email_validator.rb)0
-rw-r--r--app/validators/status_length_validator.rb (renamed from app/lib/status_length_validator.rb)0
-rw-r--r--app/validators/url_validator.rb (renamed from app/lib/url_validator.rb)0
-rw-r--r--app/views/stream_entries/show.html.haml2
-rw-r--r--config/application.rb2
-rw-r--r--config/locales/devise.nl.yml59
-rw-r--r--config/locales/doorkeeper.nl.yml129
-rw-r--r--config/locales/nl.yml165
-rw-r--r--config/locales/simple_form.nl.yml46
-rw-r--r--spec/models/export_spec.rb24
-rw-r--r--spec/spec_helper.rb4
21 files changed, 511 insertions, 10 deletions
diff --git a/.gitignore b/.gitignore
index b671bcb96..5c95f7806 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,7 @@ config/deploy/*
 
 # Ignore IDE files
 .vscode/
+
+# Ignore postgres + redis volume optionally created by docker-compose
+postgres
+redis
diff --git a/app/assets/javascripts/components/actions/compose.jsx b/app/assets/javascripts/components/actions/compose.jsx
index 1b3cc60dc..88e91c356 100644
--- a/app/assets/javascripts/components/actions/compose.jsx
+++ b/app/assets/javascripts/components/actions/compose.jsx
@@ -2,6 +2,8 @@ import api from '../api';
 
 import { updateTimeline } from './timelines';
 
+import * as emojione from 'emojione';
+
 export const COMPOSE_CHANGE          = 'COMPOSE_CHANGE';
 export const COMPOSE_SUBMIT_REQUEST  = 'COMPOSE_SUBMIT_REQUEST';
 export const COMPOSE_SUBMIT_SUCCESS  = 'COMPOSE_SUBMIT_SUCCESS';
@@ -72,9 +74,8 @@ export function mentionCompose(account, router) {
 export function submitCompose() {
   return function (dispatch, getState) {
     dispatch(submitComposeRequest());
-
     api(getState).post('/api/v1/statuses', {
-      status: getState().getIn(['compose', 'text'], ''),
+      status: emojione.shortnameToUnicode(getState().getIn(['compose', 'text'], '')),
       in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
       media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
       sensitive: getState().getIn(['compose', 'sensitive']),
diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx
index b9086de42..5cd727822 100644
--- a/app/assets/javascripts/components/containers/mastodon.jsx
+++ b/app/assets/javascripts/components/containers/mastodon.jsx
@@ -48,6 +48,7 @@ import fr from 'react-intl/locale-data/fr';
 import hu from 'react-intl/locale-data/hu';
 import ja from 'react-intl/locale-data/ja';
 import pt from 'react-intl/locale-data/pt';
+import nl from 'react-intl/locale-data/nl';
 import no from 'react-intl/locale-data/no';
 import ru from 'react-intl/locale-data/ru';
 import uk from 'react-intl/locale-data/uk';
@@ -76,6 +77,7 @@ addLocaleData([
   ...hu,
   ...ja,
   ...pt,
+  ...nl,
   ...no,
   ...ru,
   ...uk,
diff --git a/app/assets/javascripts/components/locales/bg.jsx b/app/assets/javascripts/components/locales/bg.jsx
index cac984aae..a194cdbdd 100644
--- a/app/assets/javascripts/components/locales/bg.jsx
+++ b/app/assets/javascripts/components/locales/bg.jsx
@@ -65,4 +65,4 @@ const bg = {
   "notifications.column_settings.reblog": "Споделяния:",
 };
 
-export default en;
+export default bg;
diff --git a/app/assets/javascripts/components/locales/de.jsx b/app/assets/javascripts/components/locales/de.jsx
index 92897f549..bd98b7595 100644
--- a/app/assets/javascripts/components/locales/de.jsx
+++ b/app/assets/javascripts/components/locales/de.jsx
@@ -1,4 +1,4 @@
-const en = {
+const de = {
   "column_back_button.label": "Zurück",
   "lightbox.close": "Schließen",
   "loading_indicator.label": "Lade…",
@@ -74,4 +74,4 @@ const en = {
   "missing_indicator.label": "Nicht gefunden"
 };
 
-export default en;
+export default de;
diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx
index 19a981d1d..b0403e33e 100644
--- a/app/assets/javascripts/components/locales/en.jsx
+++ b/app/assets/javascripts/components/locales/en.jsx
@@ -5,6 +5,7 @@
  *   1. to add your new string here; and
  *   2. to remove old strings that are no longer needed; and
  *   3. to sort the strings by the key.
+ #   4. To rename the `en` const name and export default name to match your locale.
  * Thanks!
  */
 const en = {
diff --git a/app/assets/javascripts/components/locales/index.jsx b/app/assets/javascripts/components/locales/index.jsx
index f14568a3d..7525022b1 100644
--- a/app/assets/javascripts/components/locales/index.jsx
+++ b/app/assets/javascripts/components/locales/index.jsx
@@ -3,6 +3,7 @@ import de from './de';
 import es from './es';
 import hu from './hu';
 import fr from './fr';
+import nl from './nl';
 import no from './no';
 import pt from './pt';
 import uk from './uk';
@@ -19,6 +20,7 @@ const locales = {
   es,
   hu,
   fr,
+  nl,
   no,
   pt,
   uk,
diff --git a/app/assets/javascripts/components/locales/nl.jsx b/app/assets/javascripts/components/locales/nl.jsx
new file mode 100644
index 000000000..cc80854fc
--- /dev/null
+++ b/app/assets/javascripts/components/locales/nl.jsx
@@ -0,0 +1,68 @@
+const nl = {
+  "column_back_button.label": "terug",
+  "lightbox.close": "Sluiten",
+  "loading_indicator.label": "Laden...",
+  "status.mention": "Vermeld @{name}",
+  "status.delete": "Verwijder",
+  "status.reply": "Reageer",
+  "status.reblog": "Boost",
+  "status.favourite": "Favoriet",
+  "status.reblogged_by": "{name} boostte",
+  "status.sensitive_warning": "Gevoelige inhoud",
+  "status.sensitive_toggle": "Klik om te zien",
+  "video_player.toggle_sound": "Geluid omschakelen",
+  "account.mention": "Vermeld @{name}",
+  "account.edit_profile": "Bewerk profiel",
+  "account.unblock": "Deblokkeer @{name}",
+  "account.unfollow": "Ontvolg",
+  "account.block": "Blokkeer @{name}",
+  "account.follow": "Volg",
+  "account.posts": "Berichten",
+  "account.follows": "Volgt",
+  "account.followers": "Volgers",
+  "account.follows_you": "Volgt jou",
+  "account.requested": "Wacht op goedkeuring",
+  "getting_started.heading": "Beginnen",
+  "getting_started.about_addressing": "Je kunt mensen volgen als je hun gebruikersnaam en het domein van hun server kent, door het e-mailachtige adres in het zoekscherm in te voeren.",
+  "getting_started.about_shortcuts": "Als de gezochte gebruiker op hetzelfde domein zit als jijzelf, is invoeren van de gebruikersnaam genoeg. Dat geldt ook als je mensen in de statussen wilt vermelden.",
+  "getting_started.open_source_notice": "Mastodon is open source software. Je kunt bijdragen of problemen melden op GitHub via {github}. {apps}.",
+  "column.home": "Thuis",
+  "column.community": "Lokale tijdlijn",
+  "column.public": "Federatietijdlijn",
+  "column.notifications": "Meldingen",
+  "tabs_bar.compose": "Schrijven",
+  "tabs_bar.home": "Thuis",
+  "tabs_bar.mentions": "Vermeldingen",
+  "tabs_bar.public": "Federatietijdlijn",
+  "tabs_bar.notifications": "Meldingen",
+  "compose_form.placeholder": "Waar ben je mee bezig?",
+  "compose_form.publish": "Toot",
+  "compose_form.sensitive": "Markeer media als gevoelig",
+  "compose_form.spoiler": "Verberg tekst achter waarschuwing",
+  "compose_form.private": "Mark als priv",
+  "compose_form.privacy_disclaimer": "Je besloten status wordt afgeleverd aan vermelde gebruikers op  {domains}. Vertrouw je {domainsCount, plural, one {that server} andere {those servers}}? Priv plaatsen werkt alleen op Mastodon servers. Als {domains} {domainsCount, plural, een {is not a Mastodon instance} andere {are not Mastodon instances}}, dan wordt er geen indicatie gegeven dat he bericht besloten is, waardoor het kan worden geboost of op andere manier zichtbaar worden voor niet bedoelde lezers.",
+  "compose_form.unlisted": "Niet tonen op openbare tijdlijnen",
+  "navigation_bar.edit_profile": "Bewerk profiel",
+  "navigation_bar.preferences": "Voorkeuren",
+  "navigation_bar.community_timeline": "Lokale tijdlijn",
+  "navigation_bar.public_timeline": "Federatietijdlijn",
+  "navigation_bar.logout": "Uitloggen",
+  "reply_indicator.cancel": "Annuleren",
+  "search.placeholder": "Zoeken",
+  "search.account": "Account",
+  "search.hashtag": "Hashtag",
+  "upload_button.label": "Toevoegen media",
+  "upload_form.undo": "Ongedaan maken",
+  "notification.follow": "{name} volgde jou",
+  "notification.favourite": "{name} markeerde je status als favoriet",
+  "notification.reblog": "{name} boostte je status",
+  "notification.mention": "{name} vermeldde jou",
+  "notifications.column_settings.alert": "Desktopmeldingen",
+  "notifications.column_settings.show": "Tonen in kolom",
+  "notifications.column_settings.follow": "Nieuwe volgers:",
+  "notifications.column_settings.favourite": "Favoriten:",
+  "notifications.column_settings.mention": "Vermeldingen:",
+  "notifications.column_settings.reblog": "Boosts:",
+};
+
+export default nl;
diff --git a/app/assets/stylesheets/stream_entries.scss b/app/assets/stylesheets/stream_entries.scss
index 4a6dc6aa4..7bd180c15 100644
--- a/app/assets/stylesheets/stream_entries.scss
+++ b/app/assets/stylesheets/stream_entries.scss
@@ -218,6 +218,7 @@
       margin-top: 8px;
       height: 300px;
       overflow: hidden;
+      position: relative;
 
       video {
         position: relative;
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index 212f88c39..c6ffe184e 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -8,6 +8,7 @@ module SettingsHelper
     eo: 'Esperanto',
     fr: 'Français',
     hu: 'Magyar',
+    nl: 'Nederlands',
     no: 'Norsk',
     pt: 'Português',
     fi: 'Suomi',
diff --git a/app/lib/email_validator.rb b/app/validators/email_validator.rb
index 06e9375f6..06e9375f6 100644
--- a/app/lib/email_validator.rb
+++ b/app/validators/email_validator.rb
diff --git a/app/lib/status_length_validator.rb b/app/validators/status_length_validator.rb
index 55135a598..55135a598 100644
--- a/app/lib/status_length_validator.rb
+++ b/app/validators/status_length_validator.rb
diff --git a/app/lib/url_validator.rb b/app/validators/url_validator.rb
index 4a5c4ef3f..4a5c4ef3f 100644
--- a/app/lib/url_validator.rb
+++ b/app/validators/url_validator.rb
diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml
index f37fb7919..862946750 100644
--- a/app/views/stream_entries/show.html.haml
+++ b/app/views/stream_entries/show.html.haml
@@ -11,7 +11,7 @@
   - else
     %meta{ property: 'og:description', content: @stream_entry.activity.content }/
 
-  - if @stream_entry.activity.is_a?(Status) && @stream_entry.activity.media_attachments.size > 0
+  - if @stream_entry.activity.is_a?(Status) && !@stream_entry.activity.sensitive? && @stream_entry.activity.media_attachments.size > 0
     %meta{ property: 'og:image', content: full_asset_url(@stream_entry.activity.media_attachments.first.file.url(:small)) }/
   - else
     %meta{ property: 'og:image', content: full_asset_url(@account.avatar.url(:original)) }/
diff --git a/config/application.rb b/config/application.rb
index 1383d45a5..f3f1f7bf7 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -24,7 +24,6 @@ module Mastodon
 
     # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
     # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
-
     config.i18n.available_locales = [
       :en,
       :bg,
@@ -35,6 +34,7 @@ module Mastodon
       :fr,
       :hu,
       :ja,
+      :nl,
       :no,
       :pt,
       :ru,
diff --git a/config/locales/devise.nl.yml b/config/locales/devise.nl.yml
new file mode 100644
index 000000000..1f5c681bd
--- /dev/null
+++ b/config/locales/devise.nl.yml
@@ -0,0 +1,59 @@
+---
+nl:
+  devise:
+    confirmations:
+      confirmed: 'Je account is bevestigd.'
+      send_instructions: 'Je ontvangt via e-mail instructies hoe je je account kan bevestigen.'
+      send_paranoid_instructions: 'Als je e-mailadres bestaat in de database, ontvang je via e-mail instructies hoe je je account kan bevestigen.'
+    failure:
+      already_authenticated: 'Je bent al ingelogd.'
+      unauthenticated: 'Je dient in te loggen of je in te schrijven.'
+      unconfirmed: 'Je dient eerst je account te bevestigen.'
+      locked: 'Je account is gelocked.'
+      invalid: 'Ongeldig e-mail of wachtwoord.'
+      invalid_token: 'Invalide authenticiteit token.'
+      timeout: 'Je sessie is verlopen, log a.u.b. opnieuw in.'
+      inactive: 'Je account is nog niet geactiveerd.'
+      last_attempt: 'Je hebt nog een poging over voordat je account wordt geblokkeerd.'
+      not_found_in_database: "Ongeldig e-mail of wachtwoord."
+    mailer:
+      confirmation_instructions:
+        subject: 'Bevestiging mailadres'
+      reset_password_instructions:
+        subject: 'Wachtwoord resetten'
+      unlock_instructions:
+        subject: 'Unlock instructies'
+    omniauth_callbacks:
+      success: 'Successvol aangemeld met je %{kind} account.'
+      failure: 'Kon je niet aanmelden met je %{kind} account, omdat "%{reason}".'
+    passwords:
+      no_token: 'Je kan deze pagina niet benaderen zonder een "wachtwoord reset e-mail"'
+      send_instructions: 'Je ontvangt via e-mail instructies hoe je je wachtwoord moet resetten.'
+      send_paranoid_instructions: 'Als je e-mailadres bestaat in de database, ontvang je via e-mail instructies hoe je je wachtwoord moet resetten.'
+      updated: 'Je wachtwoord is gewijzigd. Je bent nu ingelogd.'
+      updated_not_active: 'Je wachtwoord is gewijzigd.'
+    registrations:
+      signed_up_but_unconfirmed: 'Je ontvangt via e-mail instructies hoe je je account kunt activeren.'
+      signed_up_but_inactive: 'Je bent ingeschreven. Je kon alleen niet automatisch ingelogd worden omdat je account nog niet geactiveerd is.'
+      signed_up_but_locked: 'Je bent ingeschreven. Je kon alleen niet automatisch ingelogd worden omdat je account geblokkeerd is.'
+      signed_up: 'Je bent ingeschreven.'
+      update_needs_confirmation: 'Je hebt je e-mailadres succesvol gewijzigd, maar we moeten je nieuwe mailadres nog verifiëren. Controleer je e-mail en klik op de link in de mail om je mailadres te verifiëren.'
+      updated: 'Je account gegevens zijn opgeslagen.'
+      destroyed: 'Je account is verwijderd, wellicht tot ziens!'
+    sessions:
+      signed_in: 'Je bent succesvol ingelogd.'
+      signed_out: 'Je bent succesvol uitgelogd.'
+    unlocks:
+      send_instructions: 'Je ontvangt via e-mail instructies hoe je je account kan unlocken.'
+      send_paranoid_instructions: 'Als je e-mailadres bestaat in de database, ontvang je via e-mail instructies hoe je je account kan unlocken.'
+      unlocked: 'Je account is ge-unlocked. Je kan nu weer inloggen.'
+  errors:
+    messages:
+      already_confirmed: "is reeds bevestigd"
+      confirmation_period_expired: "moet worden bevestigd binnen %{period}, probeer het a.u.b. nog een keer"
+      expired: "is verlopen, vraag een nieuwe aan"
+      not_found: "niet gevonden"
+      not_locked: "is niet gesloten"
+      not_saved:
+        one: '1 fout blokkeerde het opslaan van deze %{resource}:'
+        other: "%{count} fouten blokkeerden het opslaan van deze %{resource}:"
diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml
new file mode 100644
index 000000000..98f2172bc
--- /dev/null
+++ b/config/locales/doorkeeper.nl.yml
@@ -0,0 +1,129 @@
+nl:
+  activerecord:
+    attributes:
+      doorkeeper/application:
+        name: 'Naam'
+        redirect_uri: 'Redirect URI'
+        scopes: 'Scopes'
+    errors:
+      models:
+        doorkeeper/application:
+          attributes:
+            redirect_uri:
+              fragment_present: 'kan geen fragment bevatten.'
+              invalid_uri: 'moet een geldige URI zijn.'
+              relative_uri: 'moet een absolute URI zijn.'
+              secured_uri: 'moet een HTTPS/SSL URI zijn.'
+
+  doorkeeper:
+    applications:
+      confirmations:
+        destroy: 'Weet je het zeker?'
+      buttons:
+        edit: 'Bewerken'
+        destroy: 'Verwijderen'
+        submit: 'Opslaan'
+        cancel: 'Annuleren'
+        authorize: 'Autoriseren'
+      form:
+        error: 'Oops! Controleer het formulier op fouten'
+      help:
+        redirect_uri: 'Gebruik één regel per URI. '
+        native_redirect_uri: 'Gebruik %{native_redirect_uri} voor lokale tests'
+        scopes: 'Scheid scopes met spaties. Laat leeg om de standaard scopes te gebruiken.'
+      edit:
+        title: 'Bewerk applicatie'
+      index:
+        title: 'Jouw applicaties'
+        new: 'Nieuwe applicatie'
+        name: 'Naam'
+        callback_url: 'Callback URL'
+      new:
+        title: 'Nieuwe applicatie'
+      show:
+        title: 'Applicatie: %{name}'
+        application_id: 'Applicatie Id'
+        secret: 'Secret'
+        scopes: 'Scopes'
+        callback_urls: 'Callback urls'
+        actions: 'Acties'
+
+    authorizations:
+      buttons:
+        authorize: 'Autoriseren'
+        deny: 'Weigeren'
+      error:
+        title: 'Er is een fout opgetreden'
+      new:
+        title: 'Autorisatie vereist'
+        prompt: '%{client_name} autoriseren om uw account te gebruiken?'
+        able_to: 'Deze applicatie zal in staat zijn om'
+      show:
+        title: 'Autorisatie code'
+
+    authorized_applications:
+      confirmations:
+        revoke: 'Weet je het zeker?'
+      buttons:
+        revoke: 'Intrekken'
+      index:
+        title: 'Jouw geautoriseerde applicaties'
+        application: 'Applicatie'
+        created_at: 'Aangemaakt op'
+        date_format: '%d-%m-%Y %H:%M:%S'
+
+    errors:
+      messages:
+        # Common error messages
+        invalid_request: 'Het verzoek mist een vereiste parameter, bevat een niet-ondersteunde parameter waarde of is anderszins onjuist.'
+        invalid_redirect_uri: 'De opgegeven redirect uri is niet geldig.'
+        unauthorized_client: 'De client is niet bevoegd om dit verzoek met deze methode uit te voeren.'
+        access_denied: 'De resource eigenaar of autorisatie-server weigerde het verzoek.'
+        invalid_scope: 'De opgevraagde scope is niet geldig, onbekend of onjuist.'
+        server_error: 'De autorisatieserver is een onverwachte voorwaarde tegengekomen die het verzoek verhinderd.'
+        temporarily_unavailable: 'De autorisatieserver is momenteel niet in staat het verzoek te behandelen als gevolg van een tijdelijke overbelasting of onderhoud aan de server.'
+
+        #configuration error messages
+        credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
+        resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'
+
+        # Access grant errors
+        unsupported_response_type: 'De autorisatieserver ondersteund dit response type niet'
+
+        # Access token errors
+        invalid_client: 'Client verificatie is mislukt door onbekende klant, geen client authenticatie opgegeven, of een niet-ondersteunde authenticatie methode.'
+        invalid_grant: 'De verstrekte autorisatie is ongeldig, verlopen, ingetrokken, komt niet overeen met de redirect uri die is opgegeven, of werd uitgegeven aan een andere klant.'
+        unsupported_grant_type: 'Het type autorisatie is niet ondersteund door de autorisatieserver'
+
+        # Password Access token errors
+        invalid_resource_owner: 'De verstrekte resource eigenaar gegevens zijn niet geldig of de resource eigenaar kan niet worden gevonden'
+
+        invalid_token:
+          revoked: "Het toegangstoken is geweigerd"
+          expired: "Het toegangstoken is verlopen"
+          unknown: "Het toegangstoken is ongeldig"
+
+    flash:
+      applications:
+        create:
+          notice: 'Applicatie aangemaakt.'
+        destroy:
+          notice: 'Applicatie verwijderd.'
+        update:
+          notice: 'Applicatie bewerkt.'
+      authorized_applications:
+        destroy:
+          notice: 'Applicatie ingetrokken.'
+
+    layouts:
+      admin:
+        nav:
+          oauth2_provider: 'OAuth2 Provider'
+          applications: 'Applicaties'
+          home: 'Home'
+      application:
+        title: 'OAuth autorisatie vereist'
+    scopes:
+      follow: volg, blokkeer, deblokkeer en stop volgen accounts
+      read: lees je accountgegevens
+      write: plaatsen namens jou
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
new file mode 100644
index 000000000..22fed2285
--- /dev/null
+++ b/config/locales/nl.yml
@@ -0,0 +1,165 @@
+---
+nl:
+  about:
+    about_mastodon: Mastodon is een <em>vrije, gratis, open-source</em> sociaal netwerk. E <em>gedecentraliseerd</em> alternatief voor commerciële platforms, het voorkomt de risico's van een enkel bedrijf dat jouw communicatie monopoliseert. Kies een server die je vertrouwt &mdash; welke je ook kiest, je kunt met iedere ander communiceren. Iedereen kan een eigen Mastodon server draaien en naadloos deelnemen in het <em>sociale netwerk</em>.
+    about_this: Over deze server
+    apps: Apps
+    business_email: 'Zakelijke e-mailadres:'
+    closed_registrations: Registrateren op deze server is momenteel uitgeschakeld.
+    contact: Contact
+    description_headline: Wat is %{domain}?
+    domain_count_after: andere servers
+    domain_count_before: Verbonden met
+    features:
+      api: Open API voor apps en services
+      blocks: Rijke blokkeer- en dempingshulpmiddelen
+      characters: 500 tekens per bericht
+      chronology: Tijdlijnen zijn chronologisch
+      ethics: 'Ethisch design: geen ads, geen spionage'
+      gifv: GIFV sets en korte video's
+      privacy: Granulaire, privacy instellingen per bericht
+      public: Openbare tijdlijnen
+    features_headline: Wat maak Mastodon anders
+    get_started: Beginnen
+    links: Links
+    other_instances: Andere servers
+    source_code: Source code
+    status_count_after: statussen
+    status_count_before: Wie schreef
+    terms: Voorw
+    user_count_after: gebruikers
+    user_count_before: Thuis naar
+  accounts:
+    follow: Volg
+    followers: Volgens
+    following: Volgend
+    nothing_here: Hier is niets!
+    people_followed_by: Mensen die %{name} volgt
+    people_who_follow: Mensen die %{name} volgen
+    posts: Berichten
+    remote_follow: Externe volg
+    unfollow: Ontvolgen
+  application_mailer:
+    settings: 'Wijzigen e-mailvoorkeuren: %{link}'
+    signature: Mastodon meldingen van %{instance}
+    view: 'Bekijk:'
+  applications:
+    invalid_url: De opgegevens URL is ongeldig
+  auth:
+    change_password: Inloggegevens
+    didnt_get_confirmation: Ontving je geen bevestigingsinstructies?
+    forgot_password: Wachtwoord vergeten?
+    login: Inloggen
+    logout: Uitloggen
+    register: Registreren
+    resend_confirmation: Herstuur de bevestigingsinstructies
+    reset_password: Herstel wachtwoord
+    set_new_password: Instellen nieuw wachtwoord
+  authorize_follow:
+    error: Helaas, er was een fout bij het opzoeken van het externe account
+    follow: Volgen
+    prompt_html: 'Je (<strong>%{self}</strong>) hebt volgen aangevraagd:'
+    title: Volg %{acct}
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count}u"
+      about_x_months: "%{count}ma"
+      about_x_years: "%{count}j"
+      almost_x_years: "%{count}j"
+      half_a_minute: Net
+      less_than_x_minutes: "%{count}m"
+      less_than_x_seconds: Net
+      over_x_years: "%{count}j"
+      x_days: "%{count}d"
+      x_minutes: "%{count}m"
+      x_months: "%{count}ma"
+      x_seconds: "%{count}s"
+  exports:
+    blocks: Je blokkeert
+    csv: CSV
+    follows: Je volgt
+    storage: Media-opslag
+  generic:
+    changes_saved_msg: Wijzigingen succesvol opgeslagen!
+    powered_by: powered by %{link}
+    save_changes: Wijziginen opslaan
+    validation_errors:
+      one: Er is iets niet helemaal goed! Bekijk onderstaande fout
+      other: Er is iets niet helemaal goed! Bekijk onderstaande %{count} fouten
+  imports:
+    preface: Je kunt bepaalde gegevens, zoals de mensen die je volgt of blokkeert, importeren voor je account op deze server, als ze zijn geëxporteerd op een andere server.
+    success: Je gegevens zijn succesvol ge-upload en wordt binnenkort verwerkt
+    types:
+      blocking: Blokkadelijst
+      following: Volglijst
+    upload: Uploaden
+  landing_strip_html: <strong>%{name}</strong> is een gebruiker op <strong>%{domain}</strong>. Je kunt deze volgen of ermee interacteren als je ergens in deze fediverse een account hebt. Als he dat niet hebt, kun je je <a href="%{sign_up_path}">hier aanmelden</a>.
+  notification_mailer:
+    digest:
+      body: 'Hier is een korte samenvatting van wat je hebt gemist op %{instance} sinds je laatste bezoek op %{since}:'
+      mention: "%{name} vermeldde je in:"
+      new_followers_summary:
+        one: Je hebt een nieuwe volger! Hoera!
+        other: Je hebt %{count} nieuwe volgers! Prachtig!
+      subject:
+        one: "1 nieuwe melding sinds je laatste bezoek \U0001F418"
+        other: "%{count} nieuwe meldingen sinds je laatste bezoek \U0001F418"
+    favourite:
+      body: 'Je status werd als favoriet gemarkeerd door  %{name}:'
+      subject: "%{name} markeerde je status als favouriet"
+    follow:
+      body: "%{name} volgt je nu!"
+      subject: "%{name} volgt je nu"
+    follow_request:
+      body: "%{name} wil je graag volgend"
+      subject: 'Volgen in afwachting: %{name}'
+    mention:
+      body: 'Je werd door %{name} vermeld in:'
+      subject: Je werd vermeld door %{name}
+    reblog:
+      body: 'Je status werd geboost door %{name}:'
+      subject: "%{name} booste je status"
+  pagination:
+    next: Volgende
+    prev: Vorige
+  remote_follow:
+    acct: Geef je gebruikersnaam@domein op waarvandaan je wilt volgen
+    missing_resource: Kon geen de vereiste doorverwijszings-URL voor je account niet vinden
+    proceed: Ga door om te volgen
+    prompt: 'Je gaat volgen:'
+  settings:
+    authorized_apps: Geautoriseerde
+    back: Terug naar Mastodon
+    edit_profile: Bewerk profiel
+    export: Gegevensexport
+    import: Import
+    preferences: Voorkeuren
+    settings: Instellingen
+    two_factor_auth: Twe-factor authenticatie
+  statuses:
+    open_in_web: Openen in web
+    over_character_limit: Tekenlimiet van %{max} overschreden
+    show_more: Toon meer
+    visibilities:
+      private: Toon alleen aan volgers
+      public: Openbaar
+      unlisted: Openbaar, maar niet tonen op openbare tijdlijn
+  stream_entries:
+    click_to_show: Klik om te tonen
+    reblogged: boostte
+    sensitive_content: Gevoelige inhoud
+  time:
+    formats:
+      default: "%b %d, %J, %U:%M"
+  two_factor_auth:
+    description_html: Als je <strong>twee-factor authenticatie</strong> instelt, kun je alleen inloggen als je je mobiele telefoon bij je hebt, waarmee je de in te voeren tokens genereert.
+    disable: Uitschakelen
+    enable: Inschakelen
+    instructions_html: "<strong>Scan deze QR-code in Google Authenticator of een soortgelijke app op je mobiele telefoon</strong>. Van nu af aan creëert deze app tokens die je bij inloggen moet invoeren."
+    plaintext_secret_html: 'Gewone-tekst geheim: <samp>%{secret}</samp>'
+    warning: Als je nu geen authenticator app kunt installeren, moet je "Uitschakelen" kiezen of je kunt niet meer inloggen.
+  users:
+    invalid_email: Het e-mailadres is ongeldig
+    invalid_otp_token: Ongeldige twe-factor code
+  will_paginate:
+    page_gap: "&hellip;"
diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml
new file mode 100644
index 000000000..5bc38a87b
--- /dev/null
+++ b/config/locales/simple_form.nl.yml
@@ -0,0 +1,46 @@
+---
+nl:
+  simple_form:
+    hints:
+      defaults:
+        avatar: PNG, GIF of JPG. Maximaal 2MB. Wordt teruggeschaald naar 120x120px
+        display_name: Maximaal 30 tekens
+        header: PNG, GIF of JPG. Maximaal 2MB. Wordt teruggeschaald naar 700x335px
+        locked: Vereist dat je handmatig volgers accepteert en stelt standaard plaatsen berichten privacy in op alleen-volgers
+        note: Maximaal 160 characters
+      imports:
+        data: CSV file geëxporteerd van een andere Mastodon server
+    labels:
+      defaults:
+        avatar: Avatar
+        confirm_new_password: Bevestig nieuw wachtwoord
+        confirm_password: Bevestig wachtwoord
+        current_password: Huidige wachtwoord
+        data: Gegevens
+        display_name: Weergavenaam
+        email: E-mailadres
+        header: Kop
+        locale: Taal
+        locked: Maak account besloten
+        new_password: Nieuwe wachtwoord
+        note: Bio
+        otp_attempt: Twee-factor code
+        password: Wachtwoord
+        setting_default_privacy: Berichten privacy
+        type: Import type
+        username: gebruikersnaam
+      interactions:
+        must_be_follower: Blokkeermeldingen van niet-volgers
+        must_be_following: Blokkeer meldingen van mensen die je niet volgt
+      notification_emails:
+        digest: Verstuur samenvattingse-mails
+        favourite: Verstuur een e-mail wanneer iemand je status als favoriet markeert
+        follow: Verstuur een e-mail wanneer iemand je volgt
+        follow_request: Verstuur een e-mail wanneer iemand je wil volgen
+        mention: Verstuur een e-mail wanneer iemand je vermeld
+        reblog: Verstuur een e-mail wanneer iemand je status boost
+    'no': 'Nee'
+    required:
+      mark: "*"
+      text: vereist
+    'yes': 'Ja'
diff --git a/spec/models/export_spec.rb b/spec/models/export_spec.rb
index 5cc62c266..3ee042fb6 100644
--- a/spec/models/export_spec.rb
+++ b/spec/models/export_spec.rb
@@ -2,12 +2,32 @@ require 'rails_helper'
 
 describe Export do
   describe 'to_csv' do
-    it 'returns a csv of the accounts' do
+    before do
       one = Account.new(username: 'one', domain: 'local.host')
       two = Account.new(username: 'two', domain: 'local.host')
       accounts = [one, two]
 
-      export = Export.new(accounts).to_csv
+      @account = double(blocking: accounts, muting: accounts, following: accounts)
+    end
+
+    it 'returns a csv of the blocked accounts' do
+      export = Export.new(@account).to_blocked_accounts_csv
+      results = export.strip.split
+
+      expect(results.size).to eq 2
+      expect(results.first).to eq 'one@local.host'
+    end
+
+    it 'returns a csv of the muted accounts' do
+      export = Export.new(@account).to_muted_accounts_csv
+      results = export.strip.split
+
+      expect(results.size).to eq 2
+      expect(results.first).to eq 'one@local.host'
+    end
+
+    it 'returns a csv of the following accounts' do
+      export = Export.new(@account).to_following_accounts_csv
       results = export.strip.split
 
       expect(results.size).to eq 2
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a5dce977d..7038efce1 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,7 +1,9 @@
 require 'simplecov'
 
 SimpleCov.start 'rails' do
-  add_group "Services", "app/services"
+  add_group 'Services', 'app/services'
+  add_group 'Presenters', 'app/presenters'
+  add_group 'Validators', 'app/validators'
 end
 
 RSpec.configure do |config|