about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch
diff options
context:
space:
mode:
authorStarfall <us@starfall.systems>2023-01-17 11:41:05 -0600
committerStarfall <us@starfall.systems>2023-01-17 11:41:05 -0600
commit1f9c919b8769f5b0a3424ef343e0049d33d656e3 (patch)
tree1853486629da4b3b76192fe8756e8d4f6d71adcb /app/javascript/flavours/glitch
parent957c21273ff42d5b2b4a5e16b7869bbb09aeb865 (diff)
parent13227e1dafd308dfe1a3effc3379b766274809b3 (diff)
Merge remote-tracking branch 'glitch/main'
Diffstat (limited to 'app/javascript/flavours/glitch')
-rw-r--r--app/javascript/flavours/glitch/actions/announcements.js4
-rw-r--r--app/javascript/flavours/glitch/actions/dropdown_menu.js4
-rw-r--r--app/javascript/flavours/glitch/components/dropdown_menu.js102
-rw-r--r--app/javascript/flavours/glitch/components/edited_timestamp/containers/dropdown_menu_container.js5
-rw-r--r--app/javascript/flavours/glitch/components/icon_button.js4
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js2
-rw-r--r--app/javascript/flavours/glitch/components/status.js8
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js43
-rw-r--r--app/javascript/flavours/glitch/containers/dropdown_menu_container.js5
-rw-r--r--app/javascript/flavours/glitch/containers/mastodon.js5
-rw-r--r--app/javascript/flavours/glitch/containers/status_container.js9
-rw-r--r--app/javascript/flavours/glitch/extra_polyfills.js3
-rw-r--r--app/javascript/flavours/glitch/features/account/components/follow_request_note.js37
-rw-r--r--app/javascript/flavours/glitch/features/account/components/header.js21
-rw-r--r--app/javascript/flavours/glitch/features/account/containers/follow_request_note_container.js15
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/components/media_item.js1
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/compose_form.js2
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/dropdown.js47
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js46
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js39
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/language_dropdown.js81
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/poll_form.js1
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/search.js88
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/options_container.js2
-rw-r--r--app/javascript/flavours/glitch/features/explore/index.js32
-rw-r--r--app/javascript/flavours/glitch/features/hashtag_timeline/index.js2
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/navigation/index.js1
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js25
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js9
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js21
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/columns_area.js2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js6
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/link_footer.js16
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_root.js5
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/video_modal.js1
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js5
-rw-r--r--app/javascript/flavours/glitch/load_polyfills.js5
-rw-r--r--app/javascript/flavours/glitch/locales/af.js7
-rw-r--r--app/javascript/flavours/glitch/locales/af.json1
-rw-r--r--app/javascript/flavours/glitch/locales/an.json1
-rw-r--r--app/javascript/flavours/glitch/locales/ar.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ar.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ast.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ast.json6
-rw-r--r--app/javascript/flavours/glitch/locales/be.json1
-rw-r--r--app/javascript/flavours/glitch/locales/bg.js7
-rw-r--r--app/javascript/flavours/glitch/locales/bg.json6
-rw-r--r--app/javascript/flavours/glitch/locales/bn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/bn.json6
-rw-r--r--app/javascript/flavours/glitch/locales/br.js7
-rw-r--r--app/javascript/flavours/glitch/locales/br.json6
-rw-r--r--app/javascript/flavours/glitch/locales/bs.json1
-rw-r--r--app/javascript/flavours/glitch/locales/ca.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ca.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ckb.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ckb.json6
-rw-r--r--app/javascript/flavours/glitch/locales/co.js7
-rw-r--r--app/javascript/flavours/glitch/locales/co.json6
-rw-r--r--app/javascript/flavours/glitch/locales/cs.js180
-rw-r--r--app/javascript/flavours/glitch/locales/cs.json153
-rw-r--r--app/javascript/flavours/glitch/locales/cy.js7
-rw-r--r--app/javascript/flavours/glitch/locales/cy.json6
-rw-r--r--app/javascript/flavours/glitch/locales/da.js7
-rw-r--r--app/javascript/flavours/glitch/locales/da.json6
-rw-r--r--app/javascript/flavours/glitch/locales/de.js7
-rw-r--r--app/javascript/flavours/glitch/locales/de.json200
-rw-r--r--app/javascript/flavours/glitch/locales/defaultMessages.json1064
-rw-r--r--app/javascript/flavours/glitch/locales/el.js7
-rw-r--r--app/javascript/flavours/glitch/locales/el.json6
-rw-r--r--app/javascript/flavours/glitch/locales/en-GB.json1
-rw-r--r--app/javascript/flavours/glitch/locales/en.js67
-rw-r--r--app/javascript/flavours/glitch/locales/en.json200
-rw-r--r--app/javascript/flavours/glitch/locales/eo.js7
-rw-r--r--app/javascript/flavours/glitch/locales/eo.json52
-rw-r--r--app/javascript/flavours/glitch/locales/es-AR.js7
-rw-r--r--app/javascript/flavours/glitch/locales/es-AR.json95
-rw-r--r--app/javascript/flavours/glitch/locales/es-MX.js7
-rw-r--r--app/javascript/flavours/glitch/locales/es-MX.json95
-rw-r--r--app/javascript/flavours/glitch/locales/es.js119
-rw-r--r--app/javascript/flavours/glitch/locales/es.json95
-rw-r--r--app/javascript/flavours/glitch/locales/et.js7
-rw-r--r--app/javascript/flavours/glitch/locales/et.json6
-rw-r--r--app/javascript/flavours/glitch/locales/eu.js7
-rw-r--r--app/javascript/flavours/glitch/locales/eu.json6
-rw-r--r--app/javascript/flavours/glitch/locales/fa.js7
-rw-r--r--app/javascript/flavours/glitch/locales/fa.json6
-rw-r--r--app/javascript/flavours/glitch/locales/fi.js7
-rw-r--r--app/javascript/flavours/glitch/locales/fi.json6
-rw-r--r--app/javascript/flavours/glitch/locales/fo.json1
-rw-r--r--app/javascript/flavours/glitch/locales/fr-QC.json200
-rw-r--r--app/javascript/flavours/glitch/locales/fr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/fr.json200
-rw-r--r--app/javascript/flavours/glitch/locales/fy.json1
-rw-r--r--app/javascript/flavours/glitch/locales/ga.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ga.json6
-rw-r--r--app/javascript/flavours/glitch/locales/gd.js7
-rw-r--r--app/javascript/flavours/glitch/locales/gd.json6
-rw-r--r--app/javascript/flavours/glitch/locales/gl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/gl.json6
-rw-r--r--app/javascript/flavours/glitch/locales/he.js7
-rw-r--r--app/javascript/flavours/glitch/locales/he.json6
-rw-r--r--app/javascript/flavours/glitch/locales/hi.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hi.json18
-rw-r--r--app/javascript/flavours/glitch/locales/hr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hr.json6
-rw-r--r--app/javascript/flavours/glitch/locales/hu.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hu.json6
-rw-r--r--app/javascript/flavours/glitch/locales/hy.js7
-rw-r--r--app/javascript/flavours/glitch/locales/hy.json6
-rw-r--r--app/javascript/flavours/glitch/locales/id.js7
-rw-r--r--app/javascript/flavours/glitch/locales/id.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ig.json1
-rw-r--r--app/javascript/flavours/glitch/locales/io.js7
-rw-r--r--app/javascript/flavours/glitch/locales/io.json6
-rw-r--r--app/javascript/flavours/glitch/locales/is.js7
-rw-r--r--app/javascript/flavours/glitch/locales/is.json6
-rw-r--r--app/javascript/flavours/glitch/locales/it.js7
-rw-r--r--app/javascript/flavours/glitch/locales/it.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ja.js158
-rw-r--r--app/javascript/flavours/glitch/locales/ja.json124
-rw-r--r--app/javascript/flavours/glitch/locales/ka.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ka.json6
-rw-r--r--app/javascript/flavours/glitch/locales/kab.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kab.json6
-rw-r--r--app/javascript/flavours/glitch/locales/kk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kk.json6
-rw-r--r--app/javascript/flavours/glitch/locales/kn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kn.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ko.js208
-rw-r--r--app/javascript/flavours/glitch/locales/ko.json200
-rw-r--r--app/javascript/flavours/glitch/locales/ku.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ku.json6
-rw-r--r--app/javascript/flavours/glitch/locales/kw.js7
-rw-r--r--app/javascript/flavours/glitch/locales/kw.json6
-rw-r--r--app/javascript/flavours/glitch/locales/la.json1
-rw-r--r--app/javascript/flavours/glitch/locales/lt.js7
-rw-r--r--app/javascript/flavours/glitch/locales/lt.json6
-rw-r--r--app/javascript/flavours/glitch/locales/lv.js7
-rw-r--r--app/javascript/flavours/glitch/locales/lv.json6
-rw-r--r--app/javascript/flavours/glitch/locales/mk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/mk.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ml.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ml.json6
-rw-r--r--app/javascript/flavours/glitch/locales/mr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/mr.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ms.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ms.json6
-rw-r--r--app/javascript/flavours/glitch/locales/my.json1
-rw-r--r--app/javascript/flavours/glitch/locales/nl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/nl.json6
-rw-r--r--app/javascript/flavours/glitch/locales/nn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/nn.json6
-rw-r--r--app/javascript/flavours/glitch/locales/no.js7
-rw-r--r--app/javascript/flavours/glitch/locales/no.json6
-rw-r--r--app/javascript/flavours/glitch/locales/oc.js7
-rw-r--r--app/javascript/flavours/glitch/locales/oc.json6
-rw-r--r--app/javascript/flavours/glitch/locales/pa.js7
-rw-r--r--app/javascript/flavours/glitch/locales/pa.json6
-rw-r--r--app/javascript/flavours/glitch/locales/pl.js79
-rw-r--r--app/javascript/flavours/glitch/locales/pl.json55
-rw-r--r--app/javascript/flavours/glitch/locales/pt-BR.js7
-rw-r--r--app/javascript/flavours/glitch/locales/pt-BR.json200
-rw-r--r--app/javascript/flavours/glitch/locales/pt-PT.js7
-rw-r--r--app/javascript/flavours/glitch/locales/pt-PT.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ro.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ro.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ru.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ru.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sa.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sa.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sc.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sc.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sco.json1
-rw-r--r--app/javascript/flavours/glitch/locales/si.js7
-rw-r--r--app/javascript/flavours/glitch/locales/si.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sk.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sl.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sq.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sq.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sr-Latn.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sr-Latn.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sr.json6
-rw-r--r--app/javascript/flavours/glitch/locales/sv.js7
-rw-r--r--app/javascript/flavours/glitch/locales/sv.json6
-rw-r--r--app/javascript/flavours/glitch/locales/szl.js7
-rw-r--r--app/javascript/flavours/glitch/locales/szl.json201
-rw-r--r--app/javascript/flavours/glitch/locales/ta.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ta.json6
-rw-r--r--app/javascript/flavours/glitch/locales/tai.js7
-rw-r--r--app/javascript/flavours/glitch/locales/tai.json201
-rw-r--r--app/javascript/flavours/glitch/locales/te.js7
-rw-r--r--app/javascript/flavours/glitch/locales/te.json6
-rw-r--r--app/javascript/flavours/glitch/locales/th.js7
-rw-r--r--app/javascript/flavours/glitch/locales/th.json6
-rw-r--r--app/javascript/flavours/glitch/locales/tr.js7
-rw-r--r--app/javascript/flavours/glitch/locales/tr.json6
-rw-r--r--app/javascript/flavours/glitch/locales/tt.js7
-rw-r--r--app/javascript/flavours/glitch/locales/tt.json6
-rw-r--r--app/javascript/flavours/glitch/locales/ug.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ug.json6
-rw-r--r--app/javascript/flavours/glitch/locales/uk.js7
-rw-r--r--app/javascript/flavours/glitch/locales/uk.json48
-rw-r--r--app/javascript/flavours/glitch/locales/ur.js7
-rw-r--r--app/javascript/flavours/glitch/locales/ur.json6
-rw-r--r--app/javascript/flavours/glitch/locales/vi.js7
-rw-r--r--app/javascript/flavours/glitch/locales/vi.json6
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_af.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ar.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ast.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_bg.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_bn.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_br.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ca.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ckb.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_co.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_cs.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_cy.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_da.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_de.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_el.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_en.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_eo.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_es-AR.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_es-MX.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_es.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_et.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_eu.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_fa.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_fi.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_fr.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ga.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_gd.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_gl.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_he.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_hi.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_hr.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_hu.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_hy.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_id.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_io.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_is.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_it.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ja.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ka.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_kab.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_kk.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_kn.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ko.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ku.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_kw.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_lt.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_lv.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_mk.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ml.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_mr.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ms.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_nl.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_nn.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_no.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_oc.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_pa.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_pl.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_pt-BR.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_pt-PT.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ro.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ru.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sa.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sc.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_si.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sk.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sl.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sq.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sr-Latn.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sr.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sv.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_szl.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ta.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_tai.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_te.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_th.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_tr.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_tt.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ug.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_uk.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ur.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_vi.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_zgh.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_zh-CN.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_zh-HK.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_zh-TW.json2
-rw-r--r--app/javascript/flavours/glitch/locales/zgh.js7
-rw-r--r--app/javascript/flavours/glitch/locales/zgh.json201
-rw-r--r--app/javascript/flavours/glitch/locales/zh-CN.js201
-rw-r--r--app/javascript/flavours/glitch/locales/zh-CN.json176
-rw-r--r--app/javascript/flavours/glitch/locales/zh-HK.js7
-rw-r--r--app/javascript/flavours/glitch/locales/zh-HK.json6
-rw-r--r--app/javascript/flavours/glitch/locales/zh-TW.js7
-rw-r--r--app/javascript/flavours/glitch/locales/zh-TW.json6
-rw-r--r--app/javascript/flavours/glitch/packs/public.js38
-rw-r--r--app/javascript/flavours/glitch/permissions.js7
-rw-r--r--app/javascript/flavours/glitch/reducers/compose.js6
-rw-r--r--app/javascript/flavours/glitch/reducers/dropdown_menu.js4
-rw-r--r--app/javascript/flavours/glitch/reducers/relationships.js11
-rw-r--r--app/javascript/flavours/glitch/selectors/index.js12
-rw-r--r--app/javascript/flavours/glitch/styles/admin.scss15
-rw-r--r--app/javascript/flavours/glitch/styles/components/accounts.scss40
-rw-r--r--app/javascript/flavours/glitch/styles/components/compose_form.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/components/emoji.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss109
-rw-r--r--app/javascript/flavours/glitch/styles/components/modal.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/components/search.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/components/single_column.scss5
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss7
-rw-r--r--app/javascript/flavours/glitch/styles/mastodon-light/diff.scss18
-rw-r--r--app/javascript/flavours/glitch/styles/modal.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/polls.scss4
-rw-r--r--app/javascript/flavours/glitch/theme.yml13
320 files changed, 4944 insertions, 1955 deletions
diff --git a/app/javascript/flavours/glitch/actions/announcements.js b/app/javascript/flavours/glitch/actions/announcements.js
index 1bdea909f..586dcfd33 100644
--- a/app/javascript/flavours/glitch/actions/announcements.js
+++ b/app/javascript/flavours/glitch/actions/announcements.js
@@ -102,7 +102,7 @@ export const addReaction = (announcementId, name) => (dispatch, getState) => {
     dispatch(addReactionRequest(announcementId, name, alreadyAdded));
   }
 
-  api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
+  api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
     dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
   }).catch(err => {
     if (!alreadyAdded) {
@@ -136,7 +136,7 @@ export const addReactionFail = (announcementId, name, error) => ({
 export const removeReaction = (announcementId, name) => (dispatch, getState) => {
   dispatch(removeReactionRequest(announcementId, name));
 
-  api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
+  api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
     dispatch(removeReactionSuccess(announcementId, name));
   }).catch(err => {
     dispatch(removeReactionFail(announcementId, name, err));
diff --git a/app/javascript/flavours/glitch/actions/dropdown_menu.js b/app/javascript/flavours/glitch/actions/dropdown_menu.js
index fb6e55612..023151d4b 100644
--- a/app/javascript/flavours/glitch/actions/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/actions/dropdown_menu.js
@@ -1,8 +1,8 @@
 export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
 export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
 
-export function openDropdownMenu(id, placement, keyboard, scroll_key) {
-  return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard, scroll_key };
+export function openDropdownMenu(id, keyboard, scroll_key) {
+  return { type: DROPDOWN_MENU_OPEN, id, keyboard, scroll_key };
 }
 
 export function closeDropdownMenu(id) {
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.js
index 036e0b909..7c70f750f 100644
--- a/app/javascript/flavours/glitch/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/components/dropdown_menu.js
@@ -2,9 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import IconButton from './icon_button';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from '../features/ui/util/optional_motion';
-import spring from 'react-motion/lib/spring';
+import Overlay from 'react-overlays/Overlay';
 import { supportsPassiveEvents } from 'detect-passive-events';
 import classNames from 'classnames';
 import { CircularProgress } from 'flavours/glitch/components/loading_indicator';
@@ -24,9 +22,6 @@ class DropdownMenu extends React.PureComponent {
     scrollable: PropTypes.bool,
     onClose: PropTypes.func.isRequired,
     style: PropTypes.object,
-    placement: PropTypes.string,
-    arrowOffsetLeft: PropTypes.string,
-    arrowOffsetTop: PropTypes.string,
     openedViaKeyboard: PropTypes.bool,
     renderItem: PropTypes.func,
     renderHeader: PropTypes.func,
@@ -35,11 +30,6 @@ class DropdownMenu extends React.PureComponent {
 
   static defaultProps = {
     style: {},
-    placement: 'bottom',
-  };
-
-  state = {
-    mounted: false,
   };
 
   handleDocumentClick = e => {
@@ -56,8 +46,6 @@ class DropdownMenu extends React.PureComponent {
     if (this.focusedItem && this.props.openedViaKeyboard) {
       this.focusedItem.focus({ preventScroll: true });
     }
-
-    this.setState({ mounted: true });
   }
 
   componentWillUnmount () {
@@ -139,40 +127,28 @@ class DropdownMenu extends React.PureComponent {
   }
 
   render () {
-    const { items, style, placement, arrowOffsetLeft, arrowOffsetTop, scrollable, renderHeader, loading } = this.props;
-    const { mounted } = this.state;
+    const { items, scrollable, renderHeader, loading } = this.props;
 
     let renderItem = this.props.renderItem || this.renderItem;
 
     return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          // It should not be transformed when mounting because the resulting
-          // size will be used to determine the coordinate of the menu by
-          // react-overlays
-          <div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
-            <div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
-
-            <div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })}>
-              {loading && (
-                <CircularProgress size={30} strokeWidth={3.5} />
-              )}
-
-              {!loading && renderHeader && (
-                <div className='dropdown-menu__container__header'>
-                  {renderHeader(items)}
-                </div>
-              )}
-
-              {!loading && (
-                <ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
-                  {items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
-                </ul>
-              )}
-            </div>
+      <div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })} ref={this.setRef}>
+        {loading && (
+          <CircularProgress size={30} strokeWidth={3.5} />
+        )}
+
+        {!loading && renderHeader && (
+          <div className='dropdown-menu__container__header'>
+            {renderHeader(items)}
           </div>
         )}
-      </Motion>
+
+        {!loading && (
+          <ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
+            {items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
+          </ul>
+        )}
+      </div>
     );
   }
 
@@ -197,7 +173,6 @@ export default class Dropdown extends React.PureComponent {
     isUserTouching: PropTypes.func,
     onOpen: PropTypes.func.isRequired,
     onClose: PropTypes.func.isRequired,
-    dropdownPlacement: PropTypes.string,
     openDropdownId: PropTypes.number,
     openedViaKeyboard: PropTypes.bool,
     renderItem: PropTypes.func,
@@ -213,13 +188,11 @@ export default class Dropdown extends React.PureComponent {
     id: id++,
   };
 
-  handleClick = ({ target, type }) => {
+  handleClick = ({ type }) => {
     if (this.state.id === this.props.openDropdownId) {
       this.handleClose();
     } else {
-      const { top } = target.getBoundingClientRect();
-      const placement = top * 2 < innerHeight ? 'bottom' : 'top';
-      this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
+      this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click');
     }
   }
 
@@ -303,7 +276,6 @@ export default class Dropdown extends React.PureComponent {
       disabled,
       loading,
       scrollable,
-      dropdownPlacement,
       openDropdownId,
       openedViaKeyboard,
       children,
@@ -314,7 +286,6 @@ export default class Dropdown extends React.PureComponent {
     const open = this.state.id === openDropdownId;
 
     const button = children ? React.cloneElement(React.Children.only(children), {
-      ref: this.setTargetRef,
       onClick: this.handleClick,
       onMouseDown: this.handleMouseDown,
       onKeyDown: this.handleButtonKeyDown,
@@ -326,7 +297,6 @@ export default class Dropdown extends React.PureComponent {
         active={open}
         disabled={disabled}
         size={size}
-        ref={this.setTargetRef}
         onClick={this.handleClick}
         onMouseDown={this.handleMouseDown}
         onKeyDown={this.handleButtonKeyDown}
@@ -336,19 +306,27 @@ export default class Dropdown extends React.PureComponent {
 
     return (
       <React.Fragment>
-        {button}
-
-        <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
-          <DropdownMenu
-            items={items}
-            loading={loading}
-            scrollable={scrollable}
-            onClose={this.handleClose}
-            openedViaKeyboard={openedViaKeyboard}
-            renderItem={renderItem}
-            renderHeader={renderHeader}
-            onItemClick={this.handleItemClick}
-          />
+        <span ref={this.setTargetRef}>
+          {button}
+        </span>
+        <Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
+          {({ props, arrowProps, placement }) => (
+            <div {...props}>
+              <div className={`dropdown-animation dropdown-menu ${placement}`}>
+                <div className={`dropdown-menu__arrow ${placement}`} {...arrowProps} />
+                <DropdownMenu
+                  items={items}
+                  loading={loading}
+                  scrollable={scrollable}
+                  onClose={this.handleClose}
+                  openedViaKeyboard={openedViaKeyboard}
+                  renderItem={renderItem}
+                  renderHeader={renderHeader}
+                  onItemClick={this.handleItemClick}
+                />
+              </div>
+            </div>
+          )}
         </Overlay>
       </React.Fragment>
     );
diff --git a/app/javascript/flavours/glitch/components/edited_timestamp/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/components/edited_timestamp/containers/dropdown_menu_container.js
index 8b73663d4..a1519757d 100644
--- a/app/javascript/flavours/glitch/components/edited_timestamp/containers/dropdown_menu_container.js
+++ b/app/javascript/flavours/glitch/components/edited_timestamp/containers/dropdown_menu_container.js
@@ -4,7 +4,6 @@ import { fetchHistory } from 'flavours/glitch/actions/history';
 import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
 
 const mapStateToProps = (state, { statusId }) => ({
-  dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
   openDropdownId: state.getIn(['dropdown_menu', 'openId']),
   openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
   items: state.getIn(['history', statusId, 'items']),
@@ -13,9 +12,9 @@ const mapStateToProps = (state, { statusId }) => ({
 
 const mapDispatchToProps = (dispatch, { statusId }) => ({
 
-  onOpen (id, onItemClick, dropdownPlacement, keyboard) {
+  onOpen (id, onItemClick, keyboard) {
     dispatch(fetchHistory(statusId));
-    dispatch(openDropdownMenu(id, dropdownPlacement, keyboard));
+    dispatch(openDropdownMenu(id, keyboard));
   },
 
   onClose (id) {
diff --git a/app/javascript/flavours/glitch/components/icon_button.js b/app/javascript/flavours/glitch/components/icon_button.js
index 41a95e92f..2485f0f48 100644
--- a/app/javascript/flavours/glitch/components/icon_button.js
+++ b/app/javascript/flavours/glitch/components/icon_button.js
@@ -30,6 +30,7 @@ export default class IconButton extends React.PureComponent {
     counter: PropTypes.number,
     obfuscateCount: PropTypes.bool,
     href: PropTypes.string,
+    ariaHidden: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -39,6 +40,7 @@ export default class IconButton extends React.PureComponent {
     animate: false,
     overlay: false,
     tabIndex: '0',
+    ariaHidden: false,
   };
 
   state = {
@@ -115,6 +117,7 @@ export default class IconButton extends React.PureComponent {
       counter,
       obfuscateCount,
       href,
+      ariaHidden,
     } = this.props;
 
     const {
@@ -155,6 +158,7 @@ export default class IconButton extends React.PureComponent {
       <button
         aria-label={title}
         aria-expanded={expanded}
+        aria-hidden={ariaHidden}
         title={title}
         className={classes}
         onClick={this.handleClick}
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index ac0d05926..23e279589 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -376,7 +376,7 @@ class MediaGallery extends React.PureComponent {
         </button>
       );
     } else if (visible) {
-      spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} />;
+      spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />;
     } else {
       spoilerButton = (
         <button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index 4041b4819..409ec0adc 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -102,7 +102,7 @@ class Status extends ImmutablePureComponent {
     scrollKey: PropTypes.string,
     deployPictureInPicture: PropTypes.func,
     settings: ImmutablePropTypes.map.isRequired,
-    pictureInPicture: PropTypes.shape({
+    pictureInPicture: ImmutablePropTypes.contains({
       inUse: PropTypes.bool,
       available: PropTypes.bool,
     }),
@@ -603,7 +603,7 @@ class Status extends ImmutablePureComponent {
 
     attachments = status.get('media_attachments');
 
-    if (pictureInPicture.inUse) {
+    if (pictureInPicture.get('inUse')) {
       media.push(<PictureInPicturePlaceholder width={this.props.cachedMediaWidth} />);
       mediaIcons.push('video-camera');
     } else if (attachments.size > 0) {
@@ -631,7 +631,7 @@ class Status extends ImmutablePureComponent {
                 width={this.props.cachedMediaWidth}
                 height={110}
                 cacheWidth={this.props.cacheMediaWidth}
-                deployPictureInPicture={pictureInPicture.available ? this.handleDeployPictureInPicture : undefined}
+                deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
                 sensitive={status.get('sensitive')}
                 blurhash={attachment.get('blurhash')}
                 visible={this.state.showMedia}
@@ -660,7 +660,7 @@ class Status extends ImmutablePureComponent {
               onOpenVideo={this.handleOpenVideo}
               width={this.props.cachedMediaWidth}
               cacheWidth={this.props.cacheMediaWidth}
-              deployPictureInPicture={pictureInPicture.available ? this.handleDeployPictureInPicture : undefined}
+              deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
               visible={this.state.showMedia}
               onToggleVisibility={this.handleToggleMediaVisibility}
             />)}
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index 977c98ccb..baf61000a 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -9,7 +9,7 @@ import { me } from 'flavours/glitch/initial_state';
 import RelativeTimestamp from './relative_timestamp';
 import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
 import classNames from 'classnames';
-import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
+import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
@@ -37,9 +37,10 @@ const messages = defineMessages({
   unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
   embed: { id: 'status.embed', defaultMessage: 'Embed' },
   admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
-  admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
-  copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
-  hide: { id: 'status.hide', defaultMessage: 'Hide toot' },
+  admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
+  admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
+  copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
+  hide: { id: 'status.hide', defaultMessage: 'Hide post' },
   edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
   filter: { id: 'status.filter', defaultMessage: 'Filter this post' },
   openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
@@ -197,6 +198,7 @@ class StatusActionBar extends ImmutablePureComponent {
 
   render () {
     const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
+    const { permissions } = this.context.identity;
 
     const anonymousAccess    = !me;
     const mutingConversation = status.get('muted');
@@ -212,11 +214,13 @@ class StatusActionBar extends ImmutablePureComponent {
 
     menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
 
+    if (publicStatus && isRemote) {
+      menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get('url') });
+    }
+
+    menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
+
     if (publicStatus) {
-      if (isRemote) {
-        menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get('url') });
-      }
-      menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
       menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
     }
 
@@ -250,19 +254,19 @@ class StatusActionBar extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
       menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
 
-      if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) {
+      if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
         menu.push(null);
-        if (accountAdminLink !== undefined) {
-          menu.push({
-            text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
-            href: accountAdminLink(status.getIn(['account', 'id'])),
-          });
+        if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
+          if (accountAdminLink !== undefined) {
+            menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
+          }
+          if (statusAdminLink !== undefined) {
+            menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
+          }
         }
-        if (statusAdminLink !== undefined) {
-          menu.push({
-            text: intl.formatMessage(messages.admin_status),
-            href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')),
-          });
+        if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
+          const domain = status.getIn(['account', 'acct']).split('@')[1];
+          menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
         }
       }
     }
@@ -326,6 +330,7 @@ class StatusActionBar extends ImmutablePureComponent {
           />
         </div>
 
+        <div className='status__action-bar-spacer' />
         <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
           <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
         </a>
diff --git a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
index b2dff63db..43ce8ca63 100644
--- a/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
+++ b/app/javascript/flavours/glitch/containers/dropdown_menu_container.js
@@ -5,18 +5,17 @@ import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
 import { isUserTouching } from '../is_mobile';
 
 const mapStateToProps = state => ({
-  dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
   openDropdownId: state.getIn(['dropdown_menu', 'openId']),
   openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
 });
 
 const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
-  onOpen(id, onItemClick, dropdownPlacement, keyboard) {
+  onOpen(id, onItemClick, keyboard) {
     dispatch(isUserTouching() ? openModal('ACTIONS', {
       status,
       actions: items,
       onClick: onItemClick,
-    }) : openDropdownMenu(id, dropdownPlacement, keyboard, scrollKey));
+    }) : openDropdownMenu(id, keyboard, scrollKey));
   },
 
   onClose(id) {
diff --git a/app/javascript/flavours/glitch/containers/mastodon.js b/app/javascript/flavours/glitch/containers/mastodon.js
index 6c92376d8..dd7623a81 100644
--- a/app/javascript/flavours/glitch/containers/mastodon.js
+++ b/app/javascript/flavours/glitch/containers/mastodon.js
@@ -27,8 +27,9 @@ store.dispatch(hydrateAction);
 // check for deprecated local settings
 store.dispatch(checkDeprecatedLocalSettings());
 
-// load custom emojis
-store.dispatch(fetchCustomEmojis());
+if (initialState.meta.me) {
+  store.dispatch(fetchCustomEmojis());
+}
 
 const createIdentityContext = state => ({
   signedIn: !!state.meta.me,
diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js
index 947573fc7..645919ebe 100644
--- a/app/javascript/flavours/glitch/containers/status_container.js
+++ b/app/javascript/flavours/glitch/containers/status_container.js
@@ -1,7 +1,7 @@
 import { connect } from 'react-redux';
 import Status from 'flavours/glitch/components/status';
 import { List as ImmutableList } from 'immutable';
-import { makeGetStatus } from 'flavours/glitch/selectors';
+import { makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors';
 import {
   replyCompose,
   mentionCompose,
@@ -60,6 +60,7 @@ const messages = defineMessages({
 
 const makeMapStateToProps = () => {
   const getStatus = makeGetStatus();
+  const getPictureInPicture = makeGetPictureInPicture();
 
   const mapStateToProps = (state, props) => {
 
@@ -83,11 +84,7 @@ const makeMapStateToProps = () => {
       account: account || props.account,
       settings: state.get('local_settings'),
       prepend: prepend || props.prepend,
-
-      pictureInPicture: {
-        inUse: state.getIn(['meta', 'layout']) !== 'mobile' && state.get('picture_in_picture').statusId === props.id,
-        available: state.getIn(['meta', 'layout']) !== 'mobile',
-      },
+      pictureInPicture: getPictureInPicture(state, props),
     };
   };
 
diff --git a/app/javascript/flavours/glitch/extra_polyfills.js b/app/javascript/flavours/glitch/extra_polyfills.js
index 0d45c23b0..6e8004f07 100644
--- a/app/javascript/flavours/glitch/extra_polyfills.js
+++ b/app/javascript/flavours/glitch/extra_polyfills.js
@@ -1,6 +1,3 @@
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
 import 'intersection-observer';
 import 'requestidlecallback';
-import objectFitImages  from 'object-fit-images';
-
-objectFitImages();
diff --git a/app/javascript/flavours/glitch/features/account/components/follow_request_note.js b/app/javascript/flavours/glitch/features/account/components/follow_request_note.js
new file mode 100644
index 000000000..73c1737a6
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/account/components/follow_request_note.js
@@ -0,0 +1,37 @@
+import React from 'react';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { FormattedMessage } from 'react-intl';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import Icon from 'flavours/glitch/components/icon';
+
+export default class FollowRequestNote extends ImmutablePureComponent {
+
+  static propTypes = {
+    account: ImmutablePropTypes.map.isRequired,
+  };
+
+  render () {
+    const { account, onAuthorize, onReject } = this.props;
+
+    return (
+      <div className='follow-request-banner'>
+        <div className='follow-request-banner__message'>
+          <FormattedMessage id='account.requested_follow' defaultMessage='{name} has requested to follow you' values={{ name: <bdi><strong dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi> }} />
+        </div>
+
+        <div className='follow-request-banner__action'>
+          <button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}>
+            <Icon id='check' fixedWidth />
+            <FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
+          </button>
+
+          <button type='button' className='button button-tertiary button--destructive' onClick={onReject}>
+            <Icon id='times' fixedWidth />
+            <FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
+          </button>
+        </div>
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js
index 9a5f2fd62..ec4a192bc 100644
--- a/app/javascript/flavours/glitch/features/account/components/header.js
+++ b/app/javascript/flavours/glitch/features/account/components/header.js
@@ -13,7 +13,8 @@ import Button from 'flavours/glitch/components/button';
 import { NavLink } from 'react-router-dom';
 import DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
 import AccountNoteContainer from '../containers/account_note_container';
-import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
+import FollowRequestNoteContainer from '../containers/follow_request_note_container';
+import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
 import { Helmet } from 'react-helmet';
 
 const messages = defineMessages({
@@ -51,6 +52,7 @@ const messages = defineMessages({
   unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
   add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
   admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
+  admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
   add_account_note: { id: 'account.add_account_note', defaultMessage: 'Add note for @{name}' },
   languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
   openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
@@ -154,7 +156,7 @@ class Header extends ImmutablePureComponent {
 
   render () {
     const { account, hidden, intl, domain } = this.props;
-    const { signedIn } = this.context.identity;
+    const { signedIn, permissions } = this.context.identity;
 
     if (!account) {
       return null;
@@ -290,9 +292,14 @@ class Header extends ImmutablePureComponent {
       }
     }
 
-    if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) {
+    if (account.get('id') !== me && ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
       menu.push(null);
-      menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) });
+      if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && accountAdminLink) {
+        menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: accountAdminLink(account.get('id')) });
+      }
+      if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
+        menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: remoteDomain }), href: `/admin/instances/${remoteDomain}` });
+      }
     }
 
     const content          = { __html: account.get('note_emojified') };
@@ -314,6 +321,8 @@ class Header extends ImmutablePureComponent {
 
     return (
       <div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+        {!(suspended || hidden || account.get('moved')) && account.getIn(['relationship', 'requested_by']) && <FollowRequestNoteContainer account={account} />}
+
         <div className='account__header__image'>
           <div className='account__header__info'>
             {info}
@@ -345,7 +354,9 @@ class Header extends ImmutablePureComponent {
           <div className='account__header__tabs__name'>
             <h1>
               <span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
-              <small>@{acct} {lockedIcon}</small>
+              <small>
+                <span>@{acct}</span> {lockedIcon}
+              </small>
             </h1>
           </div>
 
diff --git a/app/javascript/flavours/glitch/features/account/containers/follow_request_note_container.js b/app/javascript/flavours/glitch/features/account/containers/follow_request_note_container.js
new file mode 100644
index 000000000..c6a3afb7e
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/account/containers/follow_request_note_container.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux';
+import FollowRequestNote from '../components/follow_request_note';
+import { authorizeFollowRequest, rejectFollowRequest } from 'flavours/glitch/actions/accounts';
+
+const mapDispatchToProps = (dispatch, { account }) => ({
+  onAuthorize () {
+    dispatch(authorizeFollowRequest(account.get('id')));
+  },
+
+  onReject () {
+    dispatch(rejectFollowRequest(account.get('id')));
+  },
+});
+
+export default connect(null, mapDispatchToProps)(FollowRequestNote);
diff --git a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
index f7a7fd467..f20ee685e 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
@@ -104,6 +104,7 @@ export default class MediaItem extends ImmutablePureComponent {
           <video
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
+            title={attachment.get('description')}
             role='application'
             src={attachment.get('url')}
             onMouseEnter={this.handleMouseEnter}
diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.js
index abdd247a0..0462c7c4b 100644
--- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js
+++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.js
@@ -310,7 +310,7 @@ class ComposeForm extends ImmutablePureComponent {
 
         <ReplyIndicatorContainer />
 
-        <div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}>
+        <div className={`spoiler-input ${spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}>
           <AutosuggestInput
             placeholder={intl.formatMessage(messages.spoiler_placeholder)}
             value={spoilerText}
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown.js b/app/javascript/flavours/glitch/features/compose/components/dropdown.js
index 3de198c45..d98b311d9 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown.js
@@ -2,7 +2,7 @@
 import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import React from 'react';
-import Overlay from 'react-overlays/lib/Overlay';
+import Overlay from 'react-overlays/Overlay';
 
 //  Components.
 import IconButton from 'flavours/glitch/components/icon_button';
@@ -45,7 +45,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
   };
 
   //  Toggles opening and closing the dropdown.
-  handleToggle = ({ target, type }) => {
+  handleToggle = ({ type }) => {
     const { onModalOpen } = this.props;
     const { open } = this.state;
 
@@ -59,11 +59,9 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
         }
       }
     } else {
-      const { top } = target.getBoundingClientRect();
       if (this.state.open && this.activeElement) {
         this.activeElement.focus({ preventScroll: true });
       }
-      this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
       this.setState({ open: !this.state.open, openedViaKeyboard: type !== 'click' });
     }
   }
@@ -158,6 +156,18 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
     };
   }
 
+  setTargetRef = c => {
+    this.target = c;
+  }
+
+  findTarget = () => {
+    return this.target;
+  }
+
+  handleOverlayEnter = (state) => {
+    this.setState({ placement: state.placement });
+  }
+
   //  Rendering.
   render () {
     const {
@@ -179,6 +189,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
       <div
         className={classNames('privacy-dropdown', placement, { active: open })}
         onKeyDown={this.handleKeyDown}
+        ref={this.setTargetRef}
       >
         <div className={classNames('privacy-dropdown__value', { active })}>
           <IconButton
@@ -204,18 +215,26 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
           containerPadding={20}
           placement={placement}
           show={open}
-          target={this}
+          flip
+          target={this.findTarget}
           container={container}
+          popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}
         >
-          <DropdownMenu
-            items={items}
-            renderItemContents={renderItemContents}
-            onChange={onChange}
-            onClose={this.handleClose}
-            value={value}
-            openedViaKeyboard={this.state.openedViaKeyboard}
-            closeOnChange={closeOnChange}
-          />
+          {({ props, placement }) => (
+            <div {...props}>
+              <div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
+                <DropdownMenu
+                  items={items}
+                  renderItemContents={renderItemContents}
+                  onChange={onChange}
+                  onClose={this.handleClose}
+                  value={value}
+                  openedViaKeyboard={this.state.openedViaKeyboard}
+                  closeOnChange={closeOnChange}
+                />
+              </div>
+            </div>
+          )}
         </Overlay>
       </div>
     );
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
index 09e8fc35a..c4895dfd0 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
@@ -1,7 +1,6 @@
 //  Package imports.
 import PropTypes from 'prop-types';
 import React from 'react';
-import spring from 'react-motion/lib/spring';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import classNames from 'classnames';
 
@@ -10,15 +9,8 @@ import Icon from 'flavours/glitch/components/icon';
 
 //  Utils.
 import { withPassive } from 'flavours/glitch/utils/dom_helpers';
-import Motion from '../../ui/util/optional_motion';
 import { assignHandlers } from 'flavours/glitch/utils/react_helpers';
 
-//  The spring to use with our motion.
-const springMotion = spring(1, {
-  damping: 35,
-  stiffness: 400,
-});
-
 //  The component.
 export default class ComposerOptionsDropdownContent extends React.PureComponent {
 
@@ -44,7 +36,6 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
   };
 
   state = {
-    mounted: false,
     value: this.props.openedViaKeyboard ? this.props.items[0].name : undefined,
   };
 
@@ -56,7 +47,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
   }
 
   //  Stores our node in `this.node`.
-  handleRef = (node) => {
+  setRef = (node) => {
     this.node = node;
   }
 
@@ -69,7 +60,6 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
     } else {
       this.node.firstChild.focus({ preventScroll: true });
     }
-    this.setState({ mounted: true });
   }
 
   //  On unmounting, we remove our listeners.
@@ -191,7 +181,6 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
 
   //  Rendering.
   render () {
-    const { mounted } = this.state;
     const {
       items,
       onChange,
@@ -201,36 +190,9 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
 
     //  The result.
     return (
-      <Motion
-        defaultStyle={{
-          opacity: 0,
-          scaleX: 0.85,
-          scaleY: 0.75,
-        }}
-        style={{
-          opacity: springMotion,
-          scaleX: springMotion,
-          scaleY: springMotion,
-        }}
-      >
-        {({ opacity, scaleX, scaleY }) => (
-          // It should not be transformed when mounting because the resulting
-          // size will be used to determine the coordinate of the menu by
-          // react-overlays
-          <div
-            className='privacy-dropdown__dropdown'
-            ref={this.handleRef}
-            role='listbox'
-            style={{
-              ...style,
-              opacity: opacity,
-              transform: mounted ? `scale(${scaleX}, ${scaleY})` : null,
-            }}
-          >
-            {!!items && items.map((item, i) => this.renderItem(item, i))}
-          </div>
-        )}
-      </Motion>
+      <div style={{ ...style }} role='listbox' ref={this.setRef}>
+        {!!items && items.map((item, i) => this.renderItem(item, i))}
+      </div>
     );
   }
 
diff --git a/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js
index 546d398a0..38c735551 100644
--- a/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js
@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
-import Overlay from 'react-overlays/lib/Overlay';
+import Overlay from 'react-overlays/Overlay';
 import classNames from 'classnames';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { supportsPassiveEvents } from 'detect-passive-events';
@@ -155,9 +155,6 @@ class EmojiPickerMenu extends React.PureComponent {
     onClose: PropTypes.func.isRequired,
     onPick: PropTypes.func.isRequired,
     style: PropTypes.object,
-    placement: PropTypes.string,
-    arrowOffsetLeft: PropTypes.string,
-    arrowOffsetTop: PropTypes.string,
     intl: PropTypes.object.isRequired,
     skinTone: PropTypes.number.isRequired,
     onSkinTone: PropTypes.func.isRequired,
@@ -326,14 +323,13 @@ class EmojiPickerDropdown extends React.PureComponent {
   state = {
     active: false,
     loading: false,
-    placement: null,
   };
 
   setRef = (c) => {
     this.dropdown = c;
   }
 
-  onShowDropdown = ({ target }) => {
+  onShowDropdown = () => {
     this.setState({ active: true });
 
     if (!EmojiPicker) {
@@ -348,9 +344,6 @@ class EmojiPickerDropdown extends React.PureComponent {
         this.setState({ loading: false, active: false });
       });
     }
-
-    const { top } = target.getBoundingClientRect();
-    this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
   }
 
   onHideDropdown = () => {
@@ -384,7 +377,7 @@ class EmojiPickerDropdown extends React.PureComponent {
   render () {
     const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
     const title = intl.formatMessage(messages.emoji);
-    const { active, loading, placement } = this.state;
+    const { active, loading } = this.state;
 
     return (
       <div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
@@ -396,16 +389,22 @@ class EmojiPickerDropdown extends React.PureComponent {
           />}
         </div>
 
-        <Overlay show={active} placement={placement} target={this.findTarget}>
-          <EmojiPickerMenu
-            custom_emojis={this.props.custom_emojis}
-            loading={loading}
-            onClose={this.onHideDropdown}
-            onPick={onPickEmoji}
-            onSkinTone={onSkinTone}
-            skinTone={skinTone}
-            frequentlyUsedEmojis={frequentlyUsedEmojis}
-          />
+        <Overlay show={active} placement={'bottom'} target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
+          {({ props, placement })=> (
+            <div {...props} style={{ ...props.style, width: 299 }}>
+              <div className={`dropdown-animation ${placement}`}>
+                <EmojiPickerMenu
+                  custom_emojis={this.props.custom_emojis}
+                  loading={loading}
+                  onClose={this.onHideDropdown}
+                  onPick={onPickEmoji}
+                  onSkinTone={onSkinTone}
+                  skinTone={skinTone}
+                  frequentlyUsedEmojis={frequentlyUsedEmojis}
+                />
+              </div>
+            </div>
+          )}
         </Overlay>
       </div>
     );
diff --git a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js
index a3256aa9b..3a1fa0226 100644
--- a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js
@@ -2,9 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, defineMessages } from 'react-intl';
 import TextIconButton from './text_icon_button';
-import Overlay from 'react-overlays/lib/Overlay';
-import Motion from 'flavours/glitch/features/ui/util/optional_motion';
-import spring from 'react-motion/lib/spring';
+import Overlay from 'react-overlays/Overlay';
 import { supportsPassiveEvents } from 'detect-passive-events';
 import classNames from 'classnames';
 import { languages as preloadedLanguages } from 'flavours/glitch/initial_state';
@@ -22,10 +20,8 @@ const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
 class LanguageDropdownMenu extends React.PureComponent {
 
   static propTypes = {
-    style: PropTypes.object,
     value: PropTypes.string.isRequired,
     frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
-    placement: PropTypes.string.isRequired,
     onClose: PropTypes.func.isRequired,
     onChange: PropTypes.func.isRequired,
     languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
@@ -37,7 +33,6 @@ class LanguageDropdownMenu extends React.PureComponent {
   };
 
   state = {
-    mounted: false,
     searchValue: '',
   };
 
@@ -50,7 +45,6 @@ class LanguageDropdownMenu extends React.PureComponent {
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
-    this.setState({ mounted: true });
 
     // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
     // to wait for a frame before focusing
@@ -222,29 +216,22 @@ class LanguageDropdownMenu extends React.PureComponent {
   }
 
   render () {
-    const { style, placement, intl } = this.props;
-    const { mounted, searchValue } = this.state;
+    const { intl } = this.props;
+    const { searchValue } = this.state;
     const isSearching = searchValue !== '';
     const results = this.search();
 
     return (
-      <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-        {({ opacity, scaleX, scaleY }) => (
-          // It should not be transformed when mounting because the resulting
-          // size will be used to determine the coordinate of the menu by
-          // react-overlays
-          <div className={`language-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
-            <div className='emoji-mart-search'>
-              <input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
-              <button className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
-            </div>
+      <div ref={this.setRef}>
+        <div className='emoji-mart-search'>
+          <input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
+          <button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
+        </div>
 
-            <div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
-              {results.map(this.renderItem)}
-            </div>
-          </div>
-        )}
-      </Motion>
+        <div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
+          {results.map(this.renderItem)}
+        </div>
+      </div>
     );
   }
 
@@ -266,14 +253,11 @@ class LanguageDropdown extends React.PureComponent {
     placement: 'bottom',
   };
 
-  handleToggle = ({ target }) => {
-    const { top } = target.getBoundingClientRect();
-
+  handleToggle = () => {
     if (this.state.open && this.activeElement) {
       this.activeElement.focus({ preventScroll: true });
     }
 
-    this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
     this.setState({ open: !this.state.open });
   }
 
@@ -293,13 +277,25 @@ class LanguageDropdown extends React.PureComponent {
     onChange(value);
   }
 
+  setTargetRef = c => {
+    this.target = c;
+  }
+
+  findTarget = () => {
+    return this.target;
+  }
+
+  handleOverlayEnter = (state) => {
+    this.setState({ placement: state.placement });
+  }
+
   render () {
     const { value, intl, frequentlyUsedLanguages } = this.props;
     const { open, placement } = this.state;
 
     return (
-      <div className={classNames('privacy-dropdown', { active: open })}>
-        <div className='privacy-dropdown__value'>
+      <div className={classNames('privacy-dropdown', placement, { active: open })}>
+        <div className='privacy-dropdown__value' ref={this.setTargetRef} >
           <TextIconButton
             className='privacy-dropdown__value-icon'
             label={value && value.toUpperCase()}
@@ -309,15 +305,20 @@ class LanguageDropdown extends React.PureComponent {
           />
         </div>
 
-        <Overlay show={open} placement={placement} target={this}>
-          <LanguageDropdownMenu
-            value={value}
-            frequentlyUsedLanguages={frequentlyUsedLanguages}
-            onClose={this.handleClose}
-            onChange={this.handleChange}
-            placement={placement}
-            intl={intl}
-          />
+        <Overlay show={open} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
+          {({ props, placement }) => (
+            <div {...props}>
+              <div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >
+                <LanguageDropdownMenu
+                  value={value}
+                  frequentlyUsedLanguages={frequentlyUsedLanguages}
+                  onClose={this.handleClose}
+                  onChange={this.handleChange}
+                  intl={intl}
+                />
+              </div>
+            </div>
+          )}
         </Overlay>
       </div>
     );
diff --git a/app/javascript/flavours/glitch/features/compose/components/poll_form.js b/app/javascript/flavours/glitch/features/compose/components/poll_form.js
index d5edccff3..afb5da097 100644
--- a/app/javascript/flavours/glitch/features/compose/components/poll_form.js
+++ b/app/javascript/flavours/glitch/features/compose/components/poll_form.js
@@ -153,6 +153,7 @@ class PollForm extends ImmutablePureComponent {
             <option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
             <option value={3600}>{intl.formatMessage(messages.hours, { number: 1 })}</option>
             <option value={21600}>{intl.formatMessage(messages.hours, { number: 6 })}</option>
+            <option value={43200}>{intl.formatMessage(messages.hours, { number: 12 })}</option>
             <option value={86400}>{intl.formatMessage(messages.days, { number: 1 })}</option>
             <option value={259200}>{intl.formatMessage(messages.days, { number: 3 })}</option>
             <option value={604800}>{intl.formatMessage(messages.days, { number: 7 })}</option>
diff --git a/app/javascript/flavours/glitch/features/compose/components/search.js b/app/javascript/flavours/glitch/features/compose/components/search.js
index 326fe5b70..e5874de75 100644
--- a/app/javascript/flavours/glitch/features/compose/components/search.js
+++ b/app/javascript/flavours/glitch/features/compose/components/search.js
@@ -3,13 +3,12 @@ import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import React from 'react';
 import { connect } from 'react-redux';
-import spring from 'react-motion/lib/spring';
 import {
   injectIntl,
   FormattedMessage,
   defineMessages,
 } from 'react-intl';
-import Overlay from 'react-overlays/lib/Overlay';
+import Overlay from 'react-overlays/Overlay';
 
 //  Components.
 import Icon from 'flavours/glitch/components/icon';
@@ -17,7 +16,6 @@ import Icon from 'flavours/glitch/components/icon';
 //  Utils.
 import { focusRoot } from 'flavours/glitch/utils/dom_helpers';
 import { searchEnabled } from 'flavours/glitch/initial_state';
-import Motion from '../../ui/util/optional_motion';
 
 const messages = defineMessages({
   placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
@@ -26,31 +24,20 @@ const messages = defineMessages({
 
 class SearchPopout extends React.PureComponent {
 
-  static propTypes = {
-    style: PropTypes.object,
-  };
-
   render () {
-    const { style } = this.props;
     const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
     return (
-      <div style={{ ...style, position: 'absolute', width: 285, zIndex: 2 }}>
-        <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
-          {({ opacity, scaleX, scaleY }) => (
-            <div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
-              <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
-
-              <ul>
-                <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
-                <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-                <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-                <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
-              </ul>
-
-              {extraInformation}
-            </div>
-          )}
-        </Motion>
+      <div className='search-popout'>
+        <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
+
+        <ul>
+          <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
+          <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
+          <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
+          <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
+        </ul>
+
+        {extraInformation}
       </div>
     );
   }
@@ -136,6 +123,10 @@ class Search extends React.PureComponent {
     }
   }
 
+  findTarget = () => {
+    return this.searchForm;
+  }
+
   render () {
     const { intl, value, submitted } = this.props;
     const { expanded } = this.state;
@@ -144,34 +135,31 @@ class Search extends React.PureComponent {
 
     return (
       <div className='search'>
-        <label>
-          <span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
-          <input
-            ref={this.setRef}
-            className='search__input'
-            type='text'
-            placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
-            value={value || ''}
-            onChange={this.handleChange}
-            onKeyUp={this.handleKeyUp}
-            onFocus={this.handleFocus}
-            onBlur={this.handleBlur}
-          />
-        </label>
-
-        <div
-          aria-label={intl.formatMessage(messages.placeholder)}
-          className='search__icon'
-          onClick={this.handleClear}
-          role='button'
-          tabIndex='0'
-        >
+        <input
+          ref={this.setRef}
+          className='search__input'
+          type='text'
+          placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
+          aria-label={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
+          value={value || ''}
+          onChange={this.handleChange}
+          onKeyUp={this.handleKeyUp}
+          onFocus={this.handleFocus}
+          onBlur={this.handleBlur}
+        />
+
+        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
           <Icon id='search' className={hasValue ? '' : 'active'} />
           <Icon id='times-circle' className={hasValue ? 'active' : ''} />
         </div>
-
-        <Overlay show={expanded && !hasValue} placement='bottom' target={this}>
-          <SearchPopout />
+        <Overlay show={expanded && !hasValue} placement='bottom' target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
+          {({ props, placement }) => (
+            <div {...props} style={{ ...props.style, width: 285, zIndex: 2 }}>
+              <div className={`dropdown-animation ${placement}`}>
+                <SearchPopout />
+              </div>
+            </div>
+          )}
         </Overlay>
       </div>
     );
diff --git a/app/javascript/flavours/glitch/features/compose/containers/options_container.js b/app/javascript/flavours/glitch/features/compose/containers/options_container.js
index 6c3db8173..5de9f5419 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/options_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/options_container.js
@@ -46,7 +46,7 @@ const mapDispatchToProps = (dispatch) => ({
   },
 
   onDoodleOpen() {
-    dispatch(openModal('DOODLE', { noEsc: true }));
+    dispatch(openModal('DOODLE', { noEsc: true, noClose: true }));
   },
 });
 
diff --git a/app/javascript/flavours/glitch/features/explore/index.js b/app/javascript/flavours/glitch/features/explore/index.js
index ba435d7e3..da0dc7f7c 100644
--- a/app/javascript/flavours/glitch/features/explore/index.js
+++ b/app/javascript/flavours/glitch/features/explore/index.js
@@ -24,16 +24,6 @@ const mapStateToProps = state => ({
   isSearching: state.getIn(['search', 'submitted']) || !showTrends,
 });
 
-// Fix strange bug on Safari where <span> (rendered by FormattedMessage) disappears
-// after clicking around Explore top bar (issue #20885).
-// Removing width=100% from <a> also fixes it, as well as replacing <span> with <div>
-// We're choosing to wrap span with div to keep the changes local only to this tool bar.
-const WrapFormattedMessage = ({ children, ...props }) => <div><FormattedMessage {...props}>{children}</FormattedMessage></div>;
-WrapFormattedMessage.propTypes = {
-  children: PropTypes.any,
-};
-
-
 export default @connect(mapStateToProps)
 @injectIntl
 class Explore extends React.PureComponent {
@@ -78,12 +68,22 @@ class Explore extends React.PureComponent {
           {isSearching ? (
             <SearchResults />
           ) : (
-            <React.Fragment>
+            <>
               <div className='account__section-headline'>
-                <NavLink exact to='/explore'><WrapFormattedMessage id='explore.trending_statuses' defaultMessage='Posts' /></NavLink>
-                <NavLink exact to='/explore/tags'><WrapFormattedMessage id='explore.trending_tags' defaultMessage='Hashtags' /></NavLink>
-                <NavLink exact to='/explore/links'><WrapFormattedMessage id='explore.trending_links' defaultMessage='News' /></NavLink>
-                {signedIn && <NavLink exact to='/explore/suggestions'><WrapFormattedMessage id='explore.suggested_follows' defaultMessage='For you' /></NavLink>}
+                <NavLink exact to='/explore'>
+                  <FormattedMessage tagName='div' id='explore.trending_statuses' defaultMessage='Posts' />
+                </NavLink>
+                <NavLink exact to='/explore/tags'>
+                  <FormattedMessage tagName='div' id='explore.trending_tags' defaultMessage='Hashtags' />
+                </NavLink>
+                <NavLink exact to='/explore/links'>
+                  <FormattedMessage tagName='div' id='explore.trending_links' defaultMessage='News' />
+                </NavLink>
+                {signedIn && (
+                  <NavLink exact to='/explore/suggestions'>
+                    <FormattedMessage tagName='div' id='explore.suggested_follows' defaultMessage='For you' />
+                  </NavLink>
+                )}
               </div>
 
               <Switch>
@@ -97,7 +97,7 @@ class Explore extends React.PureComponent {
                 <title>{intl.formatMessage(messages.title)}</title>
                 <meta name='robots' content={isSearching ? 'noindex' : 'all'} />
               </Helmet>
-            </React.Fragment>
+            </>
           )}
         </div>
       </Column>
diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
index 4428fdecf..219dc0ec6 100644
--- a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
@@ -194,7 +194,7 @@ class HashtagTimeline extends React.PureComponent {
       const following = tag.get('following');
 
       followButton = (
-        <button className={classNames('column-header__button')} onClick={this.handleFollow} disabled={!signedIn} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)}>
+        <button className={classNames('column-header__button')} onClick={this.handleFollow} disabled={!signedIn} active={following} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)}>
           <Icon id={following ? 'user-times' : 'user-plus'} fixedWidth className='column-header__icon' />
         </button>
       );
diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
index e618a981e..98dda182f 100644
--- a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
@@ -13,7 +13,6 @@ const messages = defineMessages({
   general: {  id: 'settings.general', defaultMessage: 'General' },
   compose: {  id: 'settings.compose_box_opts', defaultMessage: 'Compose box' },
   content_warnings: { id: 'settings.content_warnings', defaultMessage: 'Content Warnings' },
-  filters: { id: 'settings.filters', defaultMessage: 'Filters' },
   collapsed: { id: 'settings.collapsed_statuses', defaultMessage: 'Collapsed toots' },
   media: { id: 'settings.media', defaultMessage: 'Media' },
   preferences: { id: 'settings.preferences', defaultMessage: 'Preferences' },
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js
index b6f8a9877..73913dd49 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -7,7 +7,7 @@ import { defineMessages, injectIntl } from 'react-intl';
 import { me } from 'flavours/glitch/initial_state';
 import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
 import classNames from 'classnames';
-import { PERMISSION_MANAGE_USERS } from 'flavours/glitch/permissions';
+import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
@@ -34,6 +34,7 @@ const messages = defineMessages({
   embed: { id: 'status.embed', defaultMessage: 'Embed' },
   admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
   admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
+  admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
   copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
   openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
 });
@@ -177,19 +178,19 @@ class ActionBar extends React.PureComponent {
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
       menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport });
-      if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) {
+      if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
         menu.push(null);
-        if (accountAdminLink !== undefined) {
-          menu.push({
-            text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }),
-            href: accountAdminLink(status.getIn(['account', 'id'])),
-          });
+        if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
+          if (accountAdminLink !== undefined) {
+            menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
+          }
+          if (statusAdminLink !== undefined) {
+            menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
+          }
         }
-        if (statusAdminLink !== undefined) {
-          menu.push({
-            text: intl.formatMessage(messages.admin_status),
-            href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')),
-          });
+        if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
+          const domain = status.getIn(['account', 'acct']).split('@')[1];
+          menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
         }
       }
     }
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
index 7d2c2aace..907fc3f1c 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -41,7 +41,10 @@ class DetailedStatus extends ImmutablePureComponent {
     domain: PropTypes.string.isRequired,
     compact: PropTypes.bool,
     showMedia: PropTypes.bool,
-    usingPiP: PropTypes.bool,
+    pictureInPicture: ImmutablePropTypes.contains({
+      inUse: PropTypes.bool,
+      available: PropTypes.bool,
+    }),
     onToggleMediaVisibility: PropTypes.func,
     intl: PropTypes.object.isRequired,
   };
@@ -120,7 +123,7 @@ class DetailedStatus extends ImmutablePureComponent {
 
   render () {
     const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
-    const { expanded, onToggleHidden, settings, usingPiP, intl } = this.props;
+    const { expanded, onToggleHidden, settings, pictureInPicture, intl } = this.props;
     const outerStyle = { boxSizing: 'border-box' };
     const { compact } = this.props;
 
@@ -153,7 +156,7 @@ class DetailedStatus extends ImmutablePureComponent {
       outerStyle.height = `${this.state.height}px`;
     }
 
-    if (usingPiP) {
+    if (pictureInPicture.get('inUse')) {
       media.push(<PictureInPicturePlaceholder />);
       mediaIcons.push('video-camera');
     } else if (status.get('media_attachments').size > 0) {
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index e190652b0..c22e7f0bd 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -41,7 +41,7 @@ import { initMuteModal } from 'flavours/glitch/actions/mutes';
 import { initBlockModal } from 'flavours/glitch/actions/blocks';
 import { initReport } from 'flavours/glitch/actions/reports';
 import { initBoostModal } from 'flavours/glitch/actions/boosts';
-import { makeGetStatus } from 'flavours/glitch/selectors';
+import { makeGetStatus, makeGetPictureInPicture } from 'flavours/glitch/selectors';
 import ScrollContainer from 'flavours/glitch/containers/scroll_container';
 import ColumnBackButton from 'flavours/glitch/components/column_back_button';
 import ColumnHeader from '../../components/column_header';
@@ -67,11 +67,12 @@ const messages = defineMessages({
   detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
-  tootHeading: { id: 'column.toot', defaultMessage: 'Toots and replies' },
+  tootHeading: { id: 'account.posts_with_replies', defaultMessage: 'Posts and replies' },
 });
 
 const makeMapStateToProps = () => {
   const getStatus = makeGetStatus();
+  const getPictureInPicture = makeGetPictureInPicture();
 
   const getAncestorsIds = createSelector([
     (_, { id }) => id,
@@ -129,11 +130,12 @@ const makeMapStateToProps = () => {
 
   const mapStateToProps = (state, props) => {
     const status = getStatus(state, { id: props.params.statusId });
-    let ancestorsIds = Immutable.List();
+
+    let ancestorsIds   = Immutable.List();
     let descendantsIds = Immutable.List();
 
     if (status) {
-      ancestorsIds = getAncestorsIds(state, { id: status.get('in_reply_to_id') });
+      ancestorsIds   = getAncestorsIds(state, { id: status.get('in_reply_to_id') });
       descendantsIds = getDescendantsIds(state, { id: status.get('id') });
     }
 
@@ -145,7 +147,7 @@ const makeMapStateToProps = () => {
       settings: state.get('local_settings'),
       askReplyConfirmation: state.getIn(['local_settings', 'confirm_before_clearing_draft']) && state.getIn(['compose', 'text']).trim().length !== 0,
       domain: state.getIn(['meta', 'domain']),
-      usingPiP: state.get('picture_in_picture').statusId === props.params.statusId,
+      pictureInPicture: getPictureInPicture(state, { id: props.params.statusId }),
     };
   };
 
@@ -190,7 +192,10 @@ class Status extends ImmutablePureComponent {
     askReplyConfirmation: PropTypes.bool,
     multiColumn: PropTypes.bool,
     domain: PropTypes.string.isRequired,
-    usingPiP: PropTypes.bool,
+    pictureInPicture: ImmutablePropTypes.contains({
+      inUse: PropTypes.bool,
+      available: PropTypes.bool,
+    }),
   };
 
   state = {
@@ -604,7 +609,7 @@ class Status extends ImmutablePureComponent {
 
   render () {
     let ancestors, descendants;
-    const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, usingPiP } = this.props;
+    const { isLoading, status, settings, ancestorsIds, descendantsIds, intl, domain, multiColumn, pictureInPicture } = this.props;
     const { fullscreen } = this.state;
 
     if (isLoading) {
@@ -682,7 +687,7 @@ class Status extends ImmutablePureComponent {
                   domain={domain}
                   showMedia={this.state.showMedia}
                   onToggleMediaVisibility={this.handleToggleMediaVisibility}
-                  usingPiP={usingPiP}
+                  pictureInPicture={pictureInPicture}
                 />
 
                 <ActionBar
diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
index bf3e79c24..993a50796 100644
--- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js
+++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.js
@@ -99,7 +99,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
       if (this.mediaQuery.removeEventListener) {
         this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
       } else {
-        this.mediaQuery.removeListener(this.handleLayouteChange);
+        this.mediaQuery.removeListener(this.handleLayoutChange);
       }
     }
   }
diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
index de330b3a1..0dd07fb76 100644
--- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
@@ -291,11 +291,11 @@ class FocalPointModal extends ImmutablePureComponent {
     let descriptionLabel = null;
 
     if (media.get('type') === 'audio') {
-      descriptionLabel = <FormattedMessage id='upload_form.audio_description' defaultMessage='Describe for people with hearing loss' />;
+      descriptionLabel = <FormattedMessage id='upload_form.audio_description' defaultMessage='Describe for people who are hard of hearing' />;
     } else if (media.get('type') === 'video') {
-      descriptionLabel = <FormattedMessage id='upload_form.video_description' defaultMessage='Describe for people with hearing loss or visual impairment' />;
+      descriptionLabel = <FormattedMessage id='upload_form.video_description' defaultMessage='Describe for people who are deaf, hard of hearing, blind or have low vision' />;
     } else {
-      descriptionLabel = <FormattedMessage id='upload_form.description' defaultMessage='Describe for the visually impaired' />;
+      descriptionLabel = <FormattedMessage id='upload_form.description' defaultMessage='Describe for people who are blind or have low vision' />;
     }
 
     let ocrMessage = '';
diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.js
index 28adf5e06..ac0c78674 100644
--- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js
+++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.js
@@ -52,6 +52,8 @@ class LinkFooter extends React.PureComponent {
     const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
     const canProfileDirectory = profileDirectory;
 
+    const DividingCircle = <span aria-hidden>{' · '}</span>;
+
     return (
       <div className='link-footer'>
         <p>
@@ -60,17 +62,17 @@ class LinkFooter extends React.PureComponent {
           <Link key='about' to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
           {canInvite && (
             <>
-              {' · '}
+              {DividingCircle}
               <a key='invites' href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
             </>
           )}
           {canProfileDirectory && (
             <>
-              {' · '}
+              {DividingCircle}
               <Link key='directory' to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
             </>
           )}
-          {' · '}
+          {DividingCircle}
           <Link key='privacy-policy' to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
         </p>
 
@@ -78,11 +80,13 @@ class LinkFooter extends React.PureComponent {
           <strong>Mastodon</strong>:
           {' '}
           <a href='https://joinmastodon.org' target='_blank'><FormattedMessage id='footer.about' defaultMessage='About' /></a>
-          {' · '}
+          {DividingCircle}
+          <a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
+          {DividingCircle}
           <Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
-          {' · '}
+          {DividingCircle}
           <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
-          {' · '}
+          {DividingCircle}
           v{version}
         </p>
       </div>
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
index d2ee28948..379f57cbb 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js
@@ -116,13 +116,16 @@ export default class ModalRoot extends React.PureComponent {
     this._modal = c;
   }
 
+  // prevent closing of modal when clicking the overlay
+  noop = () => {}
+
   render () {
     const { type, props, ignoreFocus } = this.props;
     const { backgroundColor } = this.state;
     const visible = !!type;
 
     return (
-      <Base backgroundColor={backgroundColor} onClose={this.handleClose} noEsc={props ? props.noEsc : false} ignoreFocus={ignoreFocus}>
+      <Base backgroundColor={backgroundColor} onClose={props && props.noClose ? this.noop : this.handleClose} noEsc={props ? props.noEsc : false} ignoreFocus={ignoreFocus}>
         {visible && (
           <>
             <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
diff --git a/app/javascript/flavours/glitch/features/ui/components/video_modal.js b/app/javascript/flavours/glitch/features/ui/components/video_modal.js
index 6b6e615a6..90be11e4b 100644
--- a/app/javascript/flavours/glitch/features/ui/components/video_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/video_modal.js
@@ -50,6 +50,7 @@ export default class VideoModal extends ImmutablePureComponent {
             autoPlay={options.autoPlay}
             volume={options.defaultVolume}
             onCloseVideo={onClose}
+            autoFocus
             detailed
             alt={media.get('description')}
           />
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index cb4655f7f..0daab747b 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -124,6 +124,7 @@ class Video extends React.PureComponent {
     volume: PropTypes.number,
     muted: PropTypes.bool,
     componentIndex: PropTypes.number,
+    autoFocus: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -537,7 +538,7 @@ class Video extends React.PureComponent {
   }
 
   render () {
-    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive, editable, blurhash } = this.props;
+    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
     const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
     const playerStyle = {};
@@ -635,7 +636,7 @@ class Video extends React.PureComponent {
 
           <div className='video-player__buttons-bar'>
             <div className='video-player__buttons left'>
-              <button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={detailed}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
+              <button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay} autoFocus={autoFocus}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
               <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
 
               <div className={classNames('video-player__volume', { active: this.state.hovered })} onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
diff --git a/app/javascript/flavours/glitch/load_polyfills.js b/app/javascript/flavours/glitch/load_polyfills.js
index cc5bcd18f..f5a897f75 100644
--- a/app/javascript/flavours/glitch/load_polyfills.js
+++ b/app/javascript/flavours/glitch/load_polyfills.js
@@ -23,15 +23,14 @@ function loadPolyfills() {
   );
 
   // Latest version of Firefox and Safari do not have IntersectionObserver.
-  // Edge does not have requestIdleCallback and object-fit CSS property.
+  // Edge does not have requestIdleCallback.
   // This avoids shipping them all the polyfills.
   const needsExtraPolyfills = !(
     window.AbortController &&
     window.IntersectionObserver &&
     window.IntersectionObserverEntry &&
     'isIntersecting' in IntersectionObserverEntry.prototype &&
-    window.requestIdleCallback &&
-    'object-fit' in (new Image()).style
+    window.requestIdleCallback
   );
 
   return Promise.all([
diff --git a/app/javascript/flavours/glitch/locales/af.js b/app/javascript/flavours/glitch/locales/af.js
deleted file mode 100644
index 4c97b644a..000000000
--- a/app/javascript/flavours/glitch/locales/af.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/af.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/af.json b/app/javascript/flavours/glitch/locales/af.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/af.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/an.json b/app/javascript/flavours/glitch/locales/an.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/an.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/ar.js b/app/javascript/flavours/glitch/locales/ar.js
deleted file mode 100644
index 1081147d5..000000000
--- a/app/javascript/flavours/glitch/locales/ar.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ar.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ar.json b/app/javascript/flavours/glitch/locales/ar.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ar.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ast.js b/app/javascript/flavours/glitch/locales/ast.js
deleted file mode 100644
index 41355c24c..000000000
--- a/app/javascript/flavours/glitch/locales/ast.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ast.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ast.json b/app/javascript/flavours/glitch/locales/ast.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ast.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/be.json b/app/javascript/flavours/glitch/locales/be.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/be.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/bg.js b/app/javascript/flavours/glitch/locales/bg.js
deleted file mode 100644
index 979039376..000000000
--- a/app/javascript/flavours/glitch/locales/bg.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/bg.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/bg.json b/app/javascript/flavours/glitch/locales/bg.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/bg.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/bn.js b/app/javascript/flavours/glitch/locales/bn.js
deleted file mode 100644
index a453498b3..000000000
--- a/app/javascript/flavours/glitch/locales/bn.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/bn.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/bn.json b/app/javascript/flavours/glitch/locales/bn.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/bn.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/br.js b/app/javascript/flavours/glitch/locales/br.js
deleted file mode 100644
index 966bd1b2f..000000000
--- a/app/javascript/flavours/glitch/locales/br.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/br.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/br.json b/app/javascript/flavours/glitch/locales/br.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/br.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/bs.json b/app/javascript/flavours/glitch/locales/bs.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/bs.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/ca.js b/app/javascript/flavours/glitch/locales/ca.js
deleted file mode 100644
index baf76bd6f..000000000
--- a/app/javascript/flavours/glitch/locales/ca.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ca.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ca.json b/app/javascript/flavours/glitch/locales/ca.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ca.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ckb.js b/app/javascript/flavours/glitch/locales/ckb.js
deleted file mode 100644
index c2e177d5f..000000000
--- a/app/javascript/flavours/glitch/locales/ckb.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ckb.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ckb.json b/app/javascript/flavours/glitch/locales/ckb.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ckb.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/co.js b/app/javascript/flavours/glitch/locales/co.js
deleted file mode 100644
index 6e9e46797..000000000
--- a/app/javascript/flavours/glitch/locales/co.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/co.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/co.json b/app/javascript/flavours/glitch/locales/co.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/co.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/cs.js b/app/javascript/flavours/glitch/locales/cs.js
deleted file mode 100644
index e789facb0..000000000
--- a/app/javascript/flavours/glitch/locales/cs.js
+++ /dev/null
@@ -1,180 +0,0 @@
-import inherited from 'mastodon/locales/cs.json';
-
-const messages = {
-  'about.fork_disclaimer': 'Glitch-soc je svobodný software s otevřeným zdrojovým kódem založený na Mastodonu.',
-  'settings.layout_opts': 'Možnosti rozvržení',
-  'settings.layout': 'Rozložení:',
-  'layout.current_is': 'Nastavené rozložení je:',
-  'layout.auto': 'Automatické',
-  'layout.desktop': 'Desktop',
-  'layout.mobile': 'Mobil',
-  'layout.hint.auto': 'Vybrat rozložení automaticky v závislosti na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.',
-  'layout.hint.desktop': 'Použít vícesloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.',
-  'layout.hint.single': 'Použít jednosloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.',
-  'navigation_bar.app_settings': 'Nastavení aplikace',
-  'navigation_bar.featured_users': 'Vybraní uživatelé',
-  'endorsed_accounts_editor.endorsed_accounts': 'Vybrané účty',
-  'navigation_bar.info': 'Rozšířené informace',
-  'navigation_bar.misc': 'Různé',
-  'navigation_bar.keyboard_shortcuts': 'Klávesové zkratky',
-  'getting_started.onboarding': 'Ukaž mi to tu', 
-  'onboarding.skip': 'Přeskočit',
-  'onboarding.next': 'Další',
-  'onboarding.done': 'Hotovo',
-  'onboarding.page_one.federation': '{domain} je \'instance\' Mastodonu. Mastodon je síť nezávislých serverů, které jsou spolu propojené do jedné velké sociální sítě. Těmto serverům říkáme instance.',
-  'onboarding.page_one.handle': 'Jste na instanci {domain}, takže celá adresa vašeho profilu je {handle}',
-  'onboarding.page_one.welcome': 'Vítá vás {domain}!',
-  'onboarding.page_two.compose': 'Příspěvky se píší v levém sloupci. Pomocí ikon pod příspěvkem k němu můžete připojit obrázky, změnit úroveň soukromí nebo přidat varování o obsahu.',
-  'onboarding.page_three.search': 'Pomocí vyhledávací lišty můžete hledat lidi nebo hashtagy. Pokud hledáte někoho z jiné instance, musíte použít celou adresu jeho profilu.',
-  'onboarding.page_three.profile': 'Upravte si svůj profil a nastavte si profilový obrázek, jméno, a krátký text o sobě. Naleznete tam i další možnosti nastavení.',
-  'onboarding.page_four.home': 'Domovská časová osa zobrazuje příspěvky od lidí, které sledujete.',
-  'onboarding.page_four.notifications': 'Notifikace se zobrazí, když s vámi někdo interaguje.',
-  'onboarding.page_five.public_timelines': 'Místní časová osa zobrazuje veřejné příspěvky všech uživatelů instance {domain}. Federovaná časová osa zobrazí příspěvky od všech, koho uživatelé instance {domain} sledují. Tyto veřejné časové osy jsou skvělý způsob, jak objevit nové lidi.',
-  'onboarding.page_six.almost_done': 'Skoro hotovo...',
-  'onboarding.page_six.github': 'Na serveru {domain} běží Glitchsoc. Glitchsoc je přátelský {fork} programu {Mastodon}, a je kompatibilní s jakoukoliv jinou mastodoní instancí nebo aplikací. Glitchsoc je zcela svobodný a má otevřený zdrojový kód. Na stránce {github} můžete hlásit chyby, žádat o nové funkce, nebo ke kódu vlastnoručně přispět.',
-  'onboarding.page_six.apps_available': 'Jsou dostupné {apps} pro iOS, Android i jiné platformy.',
-  'onboarding.page_six.various_app': 'mobilní aplikace',
-  'onboarding.page_six.appetoot': 'Veselé mastodonění!',
-  'settings.auto_collapse': 'Automaticky sbalit',
-  'settings.auto_collapse_all': 'Všechno',
-  'settings.auto_collapse_lengthy': 'Dlouhé příspěvky',
-  'settings.auto_collapse_media': 'Příspěvky s přílohami',
-  'settings.auto_collapse_notifications': 'Oznámení',
-  'settings.auto_collapse_reblogs': 'Boosty',
-  'settings.auto_collapse_replies': 'Odpovědi',
-  'settings.show_action_bar': 'Zobrazit ve sbalených příspěvcích tlačítka s akcemi',
-  'settings.close': 'Zavřít',
-  'settings.collapsed_statuses': 'Sbalené příspěvky',
-  'settings.confirm_boost_missing_media_description': 'Zobrazit potvrzovací dialog před boostnutím příspěvku s chybějícími popisky obrázků',
-  'boost_modal.missing_description': 'Příspěvek obsahuje obrázky bez popisků',
-  'settings.enable_collapsed': 'Povolit sbalené příspěvky',
-  'settings.enable_collapsed_hint': 'U sbalených příspěvků je část jejich obsahu skrytá, aby zabraly méně místa na obrazovce. (Tohle není stejná funkce jako varování o obsahu.)',
-  'settings.general': 'Obecné',
-  'settings.hicolor_privacy_icons': 'Barevné ikony soukromí',
-  'settings.hicolor_privacy_icons.hint': 'Zobrazit ikony úrovně soukromí příspěvků v jasných, snadno rozlišitelných barvách',
-  'settings.image_backgrounds': 'Obrázkové pozadí',
-  'settings.image_backgrounds_media': 'Náhled médií ve sbalených příspěvcích',
-  'settings.image_backgrounds_media_hint': 'Pokud jsou k příspěvku přiložena média, použije se první z nich jako pozadí', 
-  'settings.image_backgrounds_users': 'Nastavit sbaleným příspěvkům obrázkové pozadí',
-  'settings.inline_preview_cards': 'Zobrazit v časové ose náhledy externích odkazů',
-  'settings.media': 'Média',
-  'settings.media_letterbox': 'Neořezávat obrázky',
-  'settings.media_letterbox_hint': 'Místo výřezu obrázku zobrazit obrázek celý, doplněný podle potřeby o prázdné okraje',
-  'settings.media_fullwidth': 'Zobrazit náhledy v plné šířce',
-  'settings.notifications_opts': 'Možnosti oznámení',
-  'settings.notifications.tab_badge': 'Zobrazit počet nepřečtených oznámení',
-  'settings.notifications.tab_badge.hint': 'Počet nepřečtených oznámení se viditelně zobrazí na hlavní stránce (pokud není seznam oznámení viditelný)',
-  'settings.notifications.favicon_badge': 'Zobrazit počet na ikoně serveru',
-  'settings.notifications.favicon_badge.hint': 'Zobrazí počet nepřečtených oznámení na ikoně serveru',
-  'settings.preferences': 'Předvolby',
-  'settings.rewrite_mentions': 'Přepsat zmínky v zobrazených příspěvcích',
-  'settings.rewrite_mentions_no': 'Nepřepisovat zmínky',
-  'settings.rewrite_mentions_acct': 'Přepsat uživatelským jménem a doménou (pokud je účet na jiném serveru)',
-  'settings.rewrite_mentions_username': 'Přepsat uživatelským jménem',
-  'settings.show_reply_counter': 'Zobrazit odhad počtu odpovědí',
-  'settings.status_icons': 'Ikony u příspěvků',
-  'settings.status_icons_language': 'Indikace jazyk',
-  'settings.status_icons_reply': 'Indikace odpovědi',
-  'settings.status_icons_local_only': 'Indikace lokálního příspěvku',
-  'settings.status_icons_media': 'Indikace obrázků a anket',
-  'settings.status_icons_visibility': 'Indikace úrovně soukromí',
-  'settings.tag_misleading_links': 'Označit zavádějící odkazy',
-  'settings.tag_misleading_links.hint': 'Zobrazit skutečný cíl u každého odkazu, který ho explicitně nezmiňuje',
-  'settings.wide_view': 'Široké sloupce (pouze v režimu Desktop)',
-  'settings.wide_view_hint': 'Sloupce se roztáhnout, aby lépe vyplnily dostupný prostor.',
-  'settings.navbar_under': 'Navigační lišta vespod (pouze v režimu Mobil)',
-  'settings.compose_box_opts': 'Editační pole',
-  'settings.always_show_spoilers_field': 'Vždy zobrazit pole pro varování o obsahu',
-  'settings.prepend_cw_re': 'Při odpovídání přidat před varování o obsahu “re: ”',
-  'settings.preselect_on_reply': 'Při odpovědi označit uživatelská jména',
-  'settings.preselect_on_reply_hint': 'Při odpovídání na konverzaci s více účastníky se jména všech kromě prvního označí, aby šla jednoduše smazat',
-  'settings.confirm_missing_media_description': 'Zobrazit potvrzovací dialog při odesílání příspěvku, ve kterém chybí popisky obrázků',
-  'settings.confirm_before_clearing_draft': 'Zobrazit potvrzovací dialog před přepsáním právě vytvářené zprávy',
-  'settings.show_content_type_choice': 'Zobrazit volbu formátu příspěvku',
-  'settings.side_arm': 'Vedlejší odesílací tlačítko:',
-  'settings.side_arm.none': 'Žádné',
-  'settings.side_arm_reply_mode': 'Při odpovídání na příspěvek by vedlejší odesílací tlačítko mělo:',  
-  'settings.side_arm_reply_mode.keep': 'Použít svou nastavenou úroveň soukromí',
-  'settings.side_arm_reply_mode.copy': 'Použít úroveň soukromí příspěvku, na který odpovídáte',
-  'settings.side_arm_reply_mode.restrict': 'Zvýšit úroveň soukromí nejméně na úroveň příspěvku, na který odpovídáte',  
-  'settings.content_warnings': 'Varování o obsahu',
-  'settings.content_warnings_shared_state': 'Zobrazit/schovat všechny kopie naráz',
-  'settings.content_warnings_shared_state_hint': 'Tlačítko varování o obsahu bude mít efekt na všechny kopie příspěvku naráz, stejně jako na běžném Mastodonu. Nebude pak možné automaticky sbalit jakoukoliv kopii příspěvku, která má rozbalené varování o obsahu',
-  'settings.content_warnings_media_outside': 'Zobrazit obrázky a videa mimo varování o obsahu',
-  'settings.content_warnings_media_outside_hint': 'Obrázky a videa z příspěvku s varováním o obsahu se zobrazí se separátním přepínačem zobrazení, stejně jako na běžném Mastodonu.',
-  'settings.content_warnings_unfold_opts': 'Možnosti automatického rozbalení',
-  'settings.enable_content_warnings_auto_unfold': 'Vždy rozbalit příspěvky označené varováním o obsahu',
-  'settings.deprecated_setting': 'Tato možnost se nyní nastavuje v {settings_page_link}',
-  'settings.shared_settings_link': 'předvolbách Mastodonu',
-  'settings.content_warnings_filter': 'Tato varování o obsahu automaticky nerozbalovat:',
-  'settings.content_warnings.regexp': 'Regulární výraz',
-  'settings.media_reveal_behind_cw': 'Automaticky zobrazit média označená varováním o obsahu',
-  'settings.pop_in_player': 'Povolit plovoucí okno přehrávače',
-  'settings.pop_in_position': 'Pozice plovoucího okna:',
-  'settings.pop_in_left': 'Vlevo',
-  'settings.pop_in_right': 'Vpravo',
- 
-  
-  'status.collapse': 'Sbalit',
-  'status.uncollapse': 'Rozbalit',  
-  'status.in_reply_to': 'Tento příspěvek je odpověď',
-  'status.has_preview_card': 'Obsahuje náhled odkazu',
-  'status.has_pictures': 'Obsahuje obrázky',
-  'status.is_poll': 'Tento příspěvek je anketa',
-  'status.has_video': 'Obsahuje video',
-  'status.has_audio': 'Obsahuje audio',
-  'status.local_only': 'Viditelné pouze z vaší instance',
-
-  'media_gallery.sensitive': 'Citlivý obsah',
-
-  'favourite_modal.combo': 'Příště můžete pro přeskočení stisknout {combo}',
-
-  'home.column_settings.show_direct': 'Zobrazit přímé zprávy',
-
-  'notification_purge.start': 'Čistící režim',
-  'notifications.mark_as_read': 'Označit všechna oznámení jako přečtená',
-
-  'notification.markForDeletion': 'Označit pro smazání',
-  'notifications.clear': 'Vymazat všechna oznámení',
-  'notifications.marked_clear_confirmation': 'Určitě chcete trvale smazat všechna vybraná oznámení?',
-  'notifications.marked_clear': 'Smazat vybraná oznámení',
-
-  'notification_purge.btn_all': 'Vybrat\nvše',
-  'notification_purge.btn_none': 'Nevybrat\nnic',
-  'notification_purge.btn_invert': 'Obrátit\nvýběr',
-  'notification_purge.btn_apply': 'Smazat\nvybrané',
-
-  'compose.attach.upload': 'Nahrát soubor',
-  'compose.attach.doodle': 'Něco namalovat',
-  'compose.attach': 'Připojit...',
-
-  'advanced_options.local-only.short': 'Lokální příspěvek',
-  'advanced_options.local-only.long': 'Neposílat na jiné servery',
-  'advanced_options.local-only.tooltip': 'Tento příspěvek je pouze lokální',
-  'advanced_options.icon_title': 'Pokročilá nastavení',
-  'advanced_options.threaded_mode.short': 'Režim vlákna',
-  'advanced_options.threaded_mode.long': 'Po odeslání automaticky otevře pole pro odpověď',
-  'advanced_options.threaded_mode.tooltip': 'Režim vlákna je zapnutý',
-  
-  'home.column_settings.advanced': 'Pokročilé',
-  'home.column_settings.filter_regex': 'Filtrovat podle regulárních výrazů',
-  
-  'compose_form.poll.single_choice': 'Povolit jednu odpověď',
-  'compose_form.poll.multiple_choices': 'Povolit více odpovědí',
-  
-  'compose.content-type.plain': 'Prostý text',
-  'content-type.change': 'Formát příspěvku',
-  'compose_form.spoiler': 'Přidat varování o obsahu',
-  
-  'direct.group_by_conversations': 'Seskupit do konverzací',
-  'column.toot': 'Příspěvky a odpovědi',
-  'confirmation_modal.do_not_ask_again': 'Příště se už neptat',
-  
-  'keyboard_shortcuts.bookmark': 'Přidat do záložek',
-  'keyboard_shortcuts.toggle_collapse': 'Sbalit/rozbalit příspěvek',
-  'keyboard_shortcuts.secondary_toot': 'Odeslat příspěvek s druhotným nastavením soukromí',
-  
-  'column.subheading': 'Různé',
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/cs.json b/app/javascript/flavours/glitch/locales/cs.json
new file mode 100644
index 000000000..09ad53140
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/cs.json
@@ -0,0 +1,153 @@
+{
+  "about.fork_disclaimer": "Glitch-soc je svobodný software s otevřeným zdrojovým kódem založený na Mastodonu.",
+  "advanced_options.icon_title": "Pokročilá nastavení",
+  "advanced_options.local-only.long": "Neposílat na jiné servery",
+  "advanced_options.local-only.short": "Lokální příspěvek",
+  "advanced_options.local-only.tooltip": "Tento příspěvek je pouze lokální",
+  "advanced_options.threaded_mode.long": "Po odeslání automaticky otevře pole pro odpověď",
+  "advanced_options.threaded_mode.short": "Režim vlákna",
+  "advanced_options.threaded_mode.tooltip": "Režim vlákna je zapnutý",
+  "boost_modal.missing_description": "Příspěvek obsahuje obrázky bez popisků",
+  "column.subheading": "Různé",
+  "compose.attach": "Připojit...",
+  "compose.attach.doodle": "Něco namalovat",
+  "compose.attach.upload": "Nahrát soubor",
+  "compose.content-type.plain": "Prostý text",
+  "compose_form.poll.multiple_choices": "Povolit více odpovědí",
+  "compose_form.poll.single_choice": "Povolit jednu odpověď",
+  "compose_form.spoiler": "Přidat varování o obsahu",
+  "confirmation_modal.do_not_ask_again": "Příště se už neptat",
+  "content-type.change": "Formát příspěvku",
+  "direct.group_by_conversations": "Seskupit do konverzací",
+  "endorsed_accounts_editor.endorsed_accounts": "Vybrané účty",
+  "favourite_modal.combo": "Příště můžete pro přeskočení stisknout {combo}",
+  "getting_started.onboarding": "Ukaž mi to tu",
+  "home.column_settings.advanced": "Pokročilé",
+  "home.column_settings.filter_regex": "Filtrovat podle regulárních výrazů",
+  "home.column_settings.show_direct": "Zobrazit přímé zprávy",
+  "keyboard_shortcuts.bookmark": "Přidat do záložek",
+  "keyboard_shortcuts.secondary_toot": "Odeslat příspěvek s druhotným nastavením soukromí",
+  "keyboard_shortcuts.toggle_collapse": "Sbalit/rozbalit příspěvek",
+  "layout.auto": "Automatické",
+  "layout.hint.auto": "Vybrat rozložení automaticky v závislosti na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
+  "layout.hint.desktop": "Použít vícesloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
+  "layout.hint.single": "Použít jednosloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
+  "media_gallery.sensitive": "Citlivý obsah",
+  "navigation_bar.app_settings": "Nastavení aplikace",
+  "navigation_bar.featured_users": "Vybraní uživatelé",
+  "navigation_bar.info": "Rozšířené informace",
+  "navigation_bar.keyboard_shortcuts": "Klávesové zkratky",
+  "navigation_bar.misc": "Různé",
+  "notification.markForDeletion": "Označit pro smazání",
+  "notification_purge.btn_all": "Vybrat\nvše",
+  "notification_purge.btn_apply": "Smazat\nvybrané",
+  "notification_purge.btn_invert": "Obrátit\nvýběr",
+  "notification_purge.btn_none": "Nevybrat\nnic",
+  "notification_purge.start": "Čistící režim",
+  "notifications.marked_clear": "Smazat vybraná oznámení",
+  "notifications.marked_clear_confirmation": "Určitě chcete trvale smazat všechna vybraná oznámení?",
+  "onboarding.done": "Hotovo",
+  "onboarding.next": "Další",
+  "onboarding.page_five.public_timelines": "Místní časová osa zobrazuje veřejné příspěvky všech uživatelů instance {domain}. Federovaná časová osa zobrazí příspěvky od všech, koho uživatelé instance {domain} sledují. Tyto veřejné časové osy jsou skvělý způsob, jak objevit nové lidi.",
+  "onboarding.page_four.home": "Domovská časová osa zobrazuje příspěvky od lidí, které sledujete.",
+  "onboarding.page_four.notifications": "Notifikace se zobrazí, když s vámi někdo interaguje.",
+  "onboarding.page_one.federation": "{domain} je 'instance' Mastodonu. Mastodon je síť nezávislých serverů, které jsou spolu propojené do jedné velké sociální sítě. Těmto serverům říkáme instance.",
+  "onboarding.page_one.handle": "Jste na instanci {domain}, takže celá adresa vašeho profilu je {handle}",
+  "onboarding.page_one.welcome": "Vítá vás {domain}!",
+  "onboarding.page_six.almost_done": "Skoro hotovo...",
+  "onboarding.page_six.appetoot": "Veselé mastodonění!",
+  "onboarding.page_six.apps_available": "Jsou dostupné {apps} pro iOS, Android i jiné platformy.",
+  "onboarding.page_six.github": "Na serveru {domain} běží Glitchsoc. Glitchsoc je přátelský {fork} programu {Mastodon}, a je kompatibilní s jakoukoliv jinou mastodoní instancí nebo aplikací. Glitchsoc je zcela svobodný a má otevřený zdrojový kód. Na stránce {github} můžete hlásit chyby, žádat o nové funkce, nebo ke kódu vlastnoručně přispět.",
+  "onboarding.page_six.various_app": "mobilní aplikace",
+  "onboarding.page_three.profile": "Upravte si svůj profil a nastavte si profilový obrázek, jméno, a krátký text o sobě. Naleznete tam i další možnosti nastavení.",
+  "onboarding.page_three.search": "Pomocí vyhledávací lišty můžete hledat lidi nebo hashtagy. Pokud hledáte někoho z jiné instance, musíte použít celou adresu jeho profilu.",
+  "onboarding.page_two.compose": "Příspěvky se píší v levém sloupci. Pomocí ikon pod příspěvkem k němu můžete připojit obrázky, změnit úroveň soukromí nebo přidat varování o obsahu.",
+  "onboarding.skip": "Přeskočit",
+  "settings.always_show_spoilers_field": "Vždy zobrazit pole pro varování o obsahu",
+  "settings.auto_collapse": "Automaticky sbalit",
+  "settings.auto_collapse_all": "Všechno",
+  "settings.auto_collapse_lengthy": "Dlouhé příspěvky",
+  "settings.auto_collapse_media": "Příspěvky s přílohami",
+  "settings.auto_collapse_notifications": "Oznámení",
+  "settings.auto_collapse_reblogs": "Boosty",
+  "settings.auto_collapse_replies": "Odpovědi",
+  "settings.close": "Zavřít",
+  "settings.collapsed_statuses": "Sbalené příspěvky",
+  "settings.compose_box_opts": "Editační pole",
+  "settings.confirm_before_clearing_draft": "Zobrazit potvrzovací dialog před přepsáním právě vytvářené zprávy",
+  "settings.confirm_boost_missing_media_description": "Zobrazit potvrzovací dialog před boostnutím příspěvku s chybějícími popisky obrázků",
+  "settings.confirm_missing_media_description": "Zobrazit potvrzovací dialog při odesílání příspěvku, ve kterém chybí popisky obrázků",
+  "settings.content_warnings": "Varování o obsahu",
+  "settings.content_warnings.regexp": "Regulární výraz",
+  "settings.content_warnings_filter": "Tato varování o obsahu automaticky nerozbalovat:",
+  "settings.content_warnings_media_outside": "Zobrazit obrázky a videa mimo varování o obsahu",
+  "settings.content_warnings_media_outside_hint": "Obrázky a videa z příspěvku s varováním o obsahu se zobrazí se separátním přepínačem zobrazení, stejně jako na běžném Mastodonu.",
+  "settings.content_warnings_shared_state": "Zobrazit/schovat všechny kopie naráz",
+  "settings.content_warnings_shared_state_hint": "Tlačítko varování o obsahu bude mít efekt na všechny kopie příspěvku naráz, stejně jako na běžném Mastodonu. Nebude pak možné automaticky sbalit jakoukoliv kopii příspěvku, která má rozbalené varování o obsahu",
+  "settings.content_warnings_unfold_opts": "Možnosti automatického rozbalení",
+  "settings.deprecated_setting": "Tato možnost se nyní nastavuje v {settings_page_link}",
+  "settings.enable_collapsed": "Povolit sbalené příspěvky",
+  "settings.enable_collapsed_hint": "U sbalených příspěvků je část jejich obsahu skrytá, aby zabraly méně místa na obrazovce. (Tohle není stejná funkce jako varování o obsahu.)",
+  "settings.enable_content_warnings_auto_unfold": "Vždy rozbalit příspěvky označené varováním o obsahu",
+  "settings.general": "Obecné",
+  "settings.hicolor_privacy_icons": "Barevné ikony soukromí",
+  "settings.hicolor_privacy_icons.hint": "Zobrazit ikony úrovně soukromí příspěvků v jasných, snadno rozlišitelných barvách",
+  "settings.image_backgrounds": "Obrázkové pozadí",
+  "settings.image_backgrounds_media": "Náhled médií ve sbalených příspěvcích",
+  "settings.image_backgrounds_media_hint": "Pokud jsou k příspěvku přiložena média, použije se první z nich jako pozadí",
+  "settings.image_backgrounds_users": "Nastavit sbaleným příspěvkům obrázkové pozadí",
+  "settings.inline_preview_cards": "Zobrazit v časové ose náhledy externích odkazů",
+  "settings.layout": "Rozložení:",
+  "settings.layout_opts": "Možnosti rozvržení",
+  "settings.media": "Média",
+  "settings.media_fullwidth": "Zobrazit náhledy v plné šířce",
+  "settings.media_letterbox": "Neořezávat obrázky",
+  "settings.media_letterbox_hint": "Místo výřezu obrázku zobrazit obrázek celý, doplněný podle potřeby o prázdné okraje",
+  "settings.media_reveal_behind_cw": "Automaticky zobrazit média označená varováním o obsahu",
+  "settings.notifications.favicon_badge": "Zobrazit počet na ikoně serveru",
+  "settings.notifications.favicon_badge.hint": "Zobrazí počet nepřečtených oznámení na ikoně serveru",
+  "settings.notifications.tab_badge": "Zobrazit počet nepřečtených oznámení",
+  "settings.notifications.tab_badge.hint": "Počet nepřečtených oznámení se viditelně zobrazí na hlavní stránce (pokud není seznam oznámení viditelný)",
+  "settings.notifications_opts": "Možnosti oznámení",
+  "settings.pop_in_left": "Vlevo",
+  "settings.pop_in_player": "Povolit plovoucí okno přehrávače",
+  "settings.pop_in_position": "Pozice plovoucího okna:",
+  "settings.pop_in_right": "Vpravo",
+  "settings.preferences": "Předvolby",
+  "settings.prepend_cw_re": "Při odpovídání přidat před varování o obsahu “re: ”",
+  "settings.preselect_on_reply": "Při odpovědi označit uživatelská jména",
+  "settings.preselect_on_reply_hint": "Při odpovídání na konverzaci s více účastníky se jména všech kromě prvního označí, aby šla jednoduše smazat",
+  "settings.rewrite_mentions": "Přepsat zmínky v zobrazených příspěvcích",
+  "settings.rewrite_mentions_acct": "Přepsat uživatelským jménem a doménou (pokud je účet na jiném serveru)",
+  "settings.rewrite_mentions_no": "Nepřepisovat zmínky",
+  "settings.rewrite_mentions_username": "Přepsat uživatelským jménem",
+  "settings.shared_settings_link": "předvolbách Mastodonu",
+  "settings.show_action_bar": "Zobrazit ve sbalených příspěvcích tlačítka s akcemi",
+  "settings.show_content_type_choice": "Zobrazit volbu formátu příspěvku",
+  "settings.show_reply_counter": "Zobrazit odhad počtu odpovědí",
+  "settings.side_arm": "Vedlejší odesílací tlačítko:",
+  "settings.side_arm.none": "Žádné",
+  "settings.side_arm_reply_mode": "Při odpovídání na příspěvek by vedlejší odesílací tlačítko mělo:",
+  "settings.side_arm_reply_mode.copy": "Použít úroveň soukromí příspěvku, na který odpovídáte",
+  "settings.side_arm_reply_mode.keep": "Použít svou nastavenou úroveň soukromí",
+  "settings.side_arm_reply_mode.restrict": "Zvýšit úroveň soukromí nejméně na úroveň příspěvku, na který odpovídáte",
+  "settings.status_icons": "Ikony u příspěvků",
+  "settings.status_icons_language": "Indikace jazyk",
+  "settings.status_icons_local_only": "Indikace lokálního příspěvku",
+  "settings.status_icons_media": "Indikace obrázků a anket",
+  "settings.status_icons_reply": "Indikace odpovědi",
+  "settings.status_icons_visibility": "Indikace úrovně soukromí",
+  "settings.tag_misleading_links": "Označit zavádějící odkazy",
+  "settings.tag_misleading_links.hint": "Zobrazit skutečný cíl u každého odkazu, který ho explicitně nezmiňuje",
+  "settings.wide_view": "Široké sloupce (pouze v režimu Desktop)",
+  "settings.wide_view_hint": "Sloupce se roztáhnout, aby lépe vyplnily dostupný prostor.",
+  "status.collapse": "Sbalit",
+  "status.has_audio": "Obsahuje audio",
+  "status.has_pictures": "Obsahuje obrázky",
+  "status.has_preview_card": "Obsahuje náhled odkazu",
+  "status.has_video": "Obsahuje video",
+  "status.in_reply_to": "Tento příspěvek je odpověď",
+  "status.is_poll": "Tento příspěvek je anketa",
+  "status.local_only": "Viditelné pouze z vaší instance",
+  "status.uncollapse": "Rozbalit"
+}
diff --git a/app/javascript/flavours/glitch/locales/cy.js b/app/javascript/flavours/glitch/locales/cy.js
deleted file mode 100644
index 09412bd72..000000000
--- a/app/javascript/flavours/glitch/locales/cy.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/cy.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/cy.json b/app/javascript/flavours/glitch/locales/cy.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/cy.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/da.js b/app/javascript/flavours/glitch/locales/da.js
deleted file mode 100644
index 2b08806be..000000000
--- a/app/javascript/flavours/glitch/locales/da.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/da.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/da.json b/app/javascript/flavours/glitch/locales/da.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/da.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/de.js b/app/javascript/flavours/glitch/locales/de.js
deleted file mode 100644
index ce6453623..000000000
--- a/app/javascript/flavours/glitch/locales/de.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/de.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/de.json b/app/javascript/flavours/glitch/locales/de.json
new file mode 100644
index 000000000..c5e3cdb35
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/de.json
@@ -0,0 +1,200 @@
+{
+  "about.fork_disclaimer": "Glitch-soc ist freie, quelloffene Software geforkt von Mastodon.",
+  "account.add_account_note": "Notiz für @{name} hinzufügen",
+  "account.disclaimer_full": "Die folgenden Informationen könnten das Profil des Nutzers unvollständig wiedergeben.",
+  "account.follows": "Folgt",
+  "account.joined": "Beigetreten am {date}",
+  "account.suspended_disclaimer_full": "Dieser Nutzer wurde durch einen Moderator gesperrt.",
+  "account.view_full_profile": "Vollständiges Profil anzeigen",
+  "account_note.cancel": "Abbrechen",
+  "account_note.edit": "Bearbeiten",
+  "account_note.glitch_placeholder": "Kein Kommentar angegeben",
+  "account_note.save": "Speichern",
+  "advanced_options.icon_title": "Erweiterte Optionen",
+  "advanced_options.local-only.long": "Nicht auf anderen Instanzen posten",
+  "advanced_options.local-only.short": "Nur lokal",
+  "advanced_options.local-only.tooltip": "Dieser Post ist nur lokal",
+  "advanced_options.threaded_mode.long": "Öffnet automatisch eine Antwort beim Schreiben",
+  "advanced_options.threaded_mode.short": "Thread-Modus",
+  "advanced_options.threaded_mode.tooltip": "Thread-Modus aktiviert",
+  "boost_modal.missing_description": "Dieser Toot enthält Medien ohne Beschreibung",
+  "column.favourited_by": "Favorisiert von",
+  "column.heading": "Sonstiges",
+  "column.reblogged_by": "Geteilt von",
+  "column.subheading": "Sonstige Optionen",
+  "column_header.profile": "Profil",
+  "column_subheading.lists": "Listen",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Nur-lokale Toots anzeigen",
+  "compose.attach": "Anhängen...",
+  "compose.attach.doodle": "Etwas zeichnen",
+  "compose.attach.upload": "Eine Datei hochladen",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Unformatierter Text",
+  "compose_form.poll.multiple_choices": "Mehrfachauswahl erlauben",
+  "compose_form.poll.single_choice": "Eine Auswahl erlauben",
+  "compose_form.spoiler": "Text hinter Warnung verbergen",
+  "confirmation_modal.do_not_ask_again": "Nicht erneut nach Bestätigung fragen",
+  "confirmations.deprecated_settings.confirm": "Mastodon-Einstellungen verwenden",
+  "confirmations.deprecated_settings.message": "Einige der von dir verwendeten, glitch-soc-spezifischen {app_settings} wurden durch Mastodon {preferences} ersetzt und werden überschrieben:",
+  "confirmations.missing_media_description.confirm": "Trotzdem absenden",
+  "confirmations.missing_media_description.edit": "Anhänge bearbeiten",
+  "confirmations.missing_media_description.message": "Mindestens einem Anhang fehlt eine Beschreibung. Denke darüber nach, alle Anhänge für Sehbeeinträchtigte zu beschreiben, bevor du den Toot absendest.",
+  "confirmations.unfilter.author": "Urheber",
+  "confirmations.unfilter.confirm": "Anzeigen",
+  "confirmations.unfilter.edit_filter": "Filter bearbeiten",
+  "confirmations.unfilter.filters": "Passende{count, plural, one {r} other {}} Filter",
+  "content-type.change": "Inhaltstyp",
+  "direct.group_by_conversations": "Nach Unterhaltung gruppieren",
+  "endorsed_accounts_editor.endorsed_accounts": "Empfohlene Konten",
+  "favourite_modal.combo": "Mit {combo} wird dieses Fenster beim nächsten Mal nicht mehr angezeigt",
+  "getting_started.onboarding": "Führe mich herum",
+  "home.column_settings.advanced": "Erweitert",
+  "home.column_settings.filter_regex": "Mit regulären Ausdrücken herausfiltern",
+  "home.column_settings.show_direct": "Direktnachrichten anzeigen",
+  "home.settings": "Spalteneinstellungen",
+  "keyboard_shortcuts.bookmark": "zu Lesezeichen hinzufügen",
+  "keyboard_shortcuts.secondary_toot": "Toot mit sekundärer Privatsphäreeinstellung absenden",
+  "keyboard_shortcuts.toggle_collapse": "Toots ein-/ausklappen",
+  "layout.auto": "Automatisch",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatisch das Layout anhand der Einstellung \"Erweitertes Webinterface verwenden\" und Bildschirmgröße auswählen.",
+  "layout.hint.desktop": "Das mehrspaltige Layout verwenden, unabhängig von der Einstellung \"Erweitertes Webinterface verwenden\".",
+  "layout.hint.single": "Das einspaltige Layout verwenden, unabhängig von der Einstellung \"Erweitertes Webinterface verwenden\".",
+  "layout.single": "Mobil",
+  "media_gallery.sensitive": "Empfindlich",
+  "moved_to_warning": "Dieses Konto ist als verschoben zu {moved_to_link} markiert und akzeptiert daher keine neuen Follower.",
+  "navigation_bar.app_settings": "App-Einstellungen",
+  "navigation_bar.featured_users": "Empfohlene Nutzer",
+  "navigation_bar.info": "Erweiterte Informationen",
+  "navigation_bar.keyboard_shortcuts": "Tastaturkürzel",
+  "navigation_bar.misc": "Sonstiges",
+  "notification.markForDeletion": "Zum Entfernen auswählen",
+  "notification_purge.btn_all": "Alle\nauswählen",
+  "notification_purge.btn_apply": "Ausgewählte\nentfernen",
+  "notification_purge.btn_invert": "Auswahl\numkehren",
+  "notification_purge.btn_none": "Auswahl\naufheben",
+  "notification_purge.start": "Benachrichtigungen-Aufräumen-Modus starten",
+  "notifications.marked_clear": "Ausgewählte Benachrichtigungen entfernen",
+  "notifications.marked_clear_confirmation": "Möchtest du wirklich alle auswählten Benachrichtigungen für immer entfernen?",
+  "onboarding.done": "Fertig",
+  "onboarding.next": "Weiter",
+  "onboarding.page_five.public_timelines": "Die lokale Timeline zeigt öffentliche Posts von allen auf {domain}. Die föderierte Timeline zeigt öffentliche Posts von allen, denen Leute auf {domain} folgen. Das sind die öffentlichen Timelines, eine tolle Möglichkeit, neue Leute zu entdecken.",
+  "onboarding.page_four.home": "Die Startseite zeigt Posts von Leuten an, denen du folgst.",
+  "onboarding.page_four.notifications": "Die Benachrichtigungs-Spalte zeigt an, wenn jemand mit dir interagiert.",
+  "onboarding.page_one.federation": "{domain} ist eine \"Instanz\" von Mastodon. Mastodon ist ein Netzwerk aus unabhängigen Servern, die zusammen ein größeres soziales Netzwerk bilden. Diese Server nennen wir Instanzen.",
+  "onboarding.page_one.handle": "Du bist auf {domain}, also ist dein vollständiger Nutzername {handle}",
+  "onboarding.page_one.welcome": "Willkommen auf {domain}!",
+  "onboarding.page_six.admin": "Dein Instanz-Admin ist {admin}.",
+  "onboarding.page_six.almost_done": "Fast geschafft...",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "Es gibt {apps} für iOS, Android und andere Plattformen.",
+  "onboarding.page_six.github": "{domain} läuft auf glitch-soc. glitch-soc ist ein freundlicher {fork} von {Mastodon}, und ist mit jeder Mastodon-App oder -Instanz kompatibel. glitch-soc ist komplett frei und quelloffen. Auf {github} kannst du Fehler melden, Features anfragen oder Code beitragen.",
+  "onboarding.page_six.guidelines": "Community-Richtlinien",
+  "onboarding.page_six.read_guidelines": "Bitte lies die {guidelines} von {domain}!",
+  "onboarding.page_six.various_app": "mobile Apps",
+  "onboarding.page_three.profile": "Bearbeite dein Profil, um deinen Avatar, \"Über mich\" und den Anzeigenamen zu ändern. Dort findest du auch andere Einstellungen.",
+  "onboarding.page_three.search": "Benutze die Suchleiste, um Leute zu finden und Hashtags anzusehen, wie etwa {illustration} und {introductions}. Um nach einer Person zu suchen, die nicht auf dieser Instanz ist, benutze deren vollständigen Nutzername.",
+  "onboarding.page_two.compose": "Schreibe Posts in der Verfassen-Spalte. Mit den Symbolen unten kannst du Bilder hochladen, Privatsphäre-Einstellungen ändern, und Inhaltswarnungen hinzufügen.",
+  "onboarding.skip": "Überspringen",
+  "settings.always_show_spoilers_field": "Das Inhaltswarnungs-Feld immer aktivieren",
+  "settings.auto_collapse": "Automatisches Einklappen",
+  "settings.auto_collapse_all": "Alles",
+  "settings.auto_collapse_lengthy": "Lange Toots",
+  "settings.auto_collapse_media": "Toots mit Anhängen",
+  "settings.auto_collapse_notifications": "Benachrichtigungen",
+  "settings.auto_collapse_reblogs": "Geteilte Toots",
+  "settings.auto_collapse_replies": "Antworten",
+  "settings.close": "Schließen",
+  "settings.collapsed_statuses": "Eingeklappte Toots",
+  "settings.compose_box_opts": "Verfassen-Box",
+  "settings.confirm_before_clearing_draft": "Zeige einen Bestätigungsdialog, bevor der derzeitige Entwurf verworfen wird",
+  "settings.confirm_boost_missing_media_description": "Zeige einen Bestätigungsdialog, bevor Toots mit Anhängen ohne Beschreibung geteilt werden",
+  "settings.confirm_missing_media_description": "Zeige einen Bestätigungsdialog, bevor Toots mit Anhängen ohne Beschreibung abgesendet werden",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regulärer Ausdruck",
+  "settings.content_warnings_filter": "Inhaltswarnungen, die nicht ausgeklappt werden sollen:",
+  "settings.content_warnings_media_outside": "Medienanhänge außerhalb von Inhaltswarnungen anzeigen",
+  "settings.content_warnings_media_outside_hint": "Das ursprüngliche Verhalten von Mastodon wiederherstellen, in welchem Inhaltswarnungen keine Auswirkungen auf Anhänge haben",
+  "settings.content_warnings_shared_state": "Inhalt aller Kopien auf einmal ein-/ausblenden",
+  "settings.content_warnings_shared_state_hint": "Das ursprüngliche Verhalten von Mastodon wiederhertstellen, in welchem der Inhaltswarnungs-Knopf alle Kopien eines Posts auf einmal ein-/ausklappt. Das wird das automatische Einklappen jedweder Kopie eines Toots mit ausgeklappter Inhaltswarnung",
+  "settings.content_warnings_unfold_opts": "Optionen zum automatischen Ausklappen",
+  "settings.deprecated_setting": "Diese Einstellung wird nun von Mastodons {settings_page_link} gesteuert",
+  "settings.enable_collapsed": "Eingeklappte Toots aktivieren",
+  "settings.enable_collapsed_hint": "Eingeklappte Posts haben einen Teil ihres Inhalts verborgen, um weniger Platz am Bildschirm einzunehmen. Das passiert unabhängig von der Inhaltswarnfunktion",
+  "settings.enable_content_warnings_auto_unfold": "Inhaltswarnungen automatisch ausklappen",
+  "settings.general": "Allgemein",
+  "settings.hicolor_privacy_icons": "Eingefärbte Privatsphäre-Symbole",
+  "settings.hicolor_privacy_icons.hint": "Zeige Privatsphäre-Symbole in hellen und leicht zu unterscheidenden Farben",
+  "settings.image_backgrounds": "Bildhintergründe",
+  "settings.image_backgrounds_media": "Vorschau eingeklappter Toot-Anhänge",
+  "settings.image_backgrounds_media_hint": "Wenn der Post Anhänge hat, wird der erste als Hintergrund verwendet",
+  "settings.image_backgrounds_users": "Eingeklappten Toots einen Bild-Hintergrund geben",
+  "settings.inline_preview_cards": "Eingebettete Vorschaukarten für externe Links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout-Optionen",
+  "settings.media": "Medien",
+  "settings.media_fullwidth": "Medienvorschau in voller Breite",
+  "settings.media_letterbox": "Mediengröße anpassen",
+  "settings.media_letterbox_hint": "Medien runterskalieren und einpassen um die Bildbehälter zu füllen anstatt zu strecken und zuzuschneiden",
+  "settings.media_reveal_behind_cw": "Empfindliche Medien hinter Inhaltswarnungen standardmäßig anzeigen",
+  "settings.notifications.favicon_badge": "Favicon-Badge für ungelesene Benachrichtigungen",
+  "settings.notifications.favicon_badge.hint": "Ein Badge für ungelesene Benachrichtigungen zum Favicon hinzufügen",
+  "settings.notifications.tab_badge": "Badge für ungelesene Benachrichtigungen",
+  "settings.notifications.tab_badge.hint": "Ein Badge für ungelesene Benachrichtigungen in den Spaltensymbolen anzeigen, wenn die Benachrichtigungen nicht offen sind",
+  "settings.notifications_opts": "Benachrichtigungsoptionen",
+  "settings.pop_in_left": "Links",
+  "settings.pop_in_player": "Pop-In-Player aktivieren",
+  "settings.pop_in_position": "Position des Pop-In-Players:",
+  "settings.pop_in_right": "Rechts",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "\"re: \" beim Antworten an Inhaltswarnung voranstellen",
+  "settings.preselect_on_reply": "Nutzernamen bei Antwort vorauswählen",
+  "settings.preselect_on_reply_hint": "Beim Antworten auf eine Konversation alle Nutzernamen auswählen, die nach dem ersten kommen",
+  "settings.rewrite_mentions": "Erwähnungen in angezeigten Status umschreiben",
+  "settings.rewrite_mentions_acct": "Mit Nutzernamen und Domain umschreiben (wenn das Konto auf einer anderen Instanz ist)",
+  "settings.rewrite_mentions_no": "Erwähnungen nicht umschreiben",
+  "settings.rewrite_mentions_username": "Mit Nutzername umschreiben",
+  "settings.shared_settings_link": "Nutzereinstellungen",
+  "settings.show_action_bar": "Aktions-Knöpfe in eingeklappten Toots anzeigen",
+  "settings.show_content_type_choice": "Auswahl für die Inhaltsart beim Verfassen von Toots anzeigen",
+  "settings.show_reply_counter": "Schätzung der Antwortanzahl anzeigen",
+  "settings.side_arm": "Sekundärer Toot-Knopf:",
+  "settings.side_arm.none": "Nichts",
+  "settings.side_arm_reply_mode": "Beim Antworten auf einen Toot sollte der sekundäre Toot-Knopf:",
+  "settings.side_arm_reply_mode.copy": "Privatsphäre-Einstellung des zu beantwortenden Toot verwenden",
+  "settings.side_arm_reply_mode.keep": "Die eingestellte Privatsphäre beibehalten",
+  "settings.side_arm_reply_mode.restrict": "Privatsphäre-Einstellung auf die des zu beantwortenden Toot beschränken",
+  "settings.status_icons": "Toot-Symbole",
+  "settings.status_icons_language": "Sprach-Indikator",
+  "settings.status_icons_local_only": "\"nur Lokal\"-Indikator",
+  "settings.status_icons_media": "Medien- und Umfragen-Indikatoren",
+  "settings.status_icons_reply": "Antwort-Indikator",
+  "settings.status_icons_visibility": "Toot-Privatsphäre-Indikator",
+  "settings.swipe_to_change_columns": "Das Wechseln der Spalte durch Wischen erlauben (nur für die mobile Ansicht)",
+  "settings.tag_misleading_links": "Irreführende Links markieren",
+  "settings.tag_misleading_links.hint": "Füge eine visuelle Indikation mit dem Ziel-Host des Links zu jedem Link hinzu, bei dem dieser nicht explizit genannt wird",
+  "settings.wide_view": "Breite Ansicht (nur für den Desktop-Modus)",
+  "settings.wide_view_hint": "Verbreitert Spalten, um den verfügbaren Platz besser zu füllen.",
+  "status.collapse": "Einklappen",
+  "status.has_audio": "Hat angehängte Audiodateien",
+  "status.has_pictures": "Hat angehängte Bilder",
+  "status.has_preview_card": "Hat eine Vorschaukarte",
+  "status.has_video": "Hat angehängte Videos",
+  "status.in_reply_to": "Dieser Toot ist eine Antwort",
+  "status.is_poll": "Dieser Toot ist eine Umfrage",
+  "status.local_only": "Nur auf deiner Instanz sichtbar",
+  "status.sensitive_toggle": "Zum Anzeigen klicken",
+  "status.uncollapse": "Ausklappen",
+  "web_app_crash.change_your_settings": "Deine {settings} ändern",
+  "web_app_crash.content": "Du kannst folgende Dinge ausprobieren:",
+  "web_app_crash.debug_info": "Debug-Informationen",
+  "web_app_crash.disable_addons": "Browser-Add-ons oder eingebaute Übersetzungswerkzeuge deaktivieren",
+  "web_app_crash.issue_tracker": "Issue-Tracker",
+  "web_app_crash.reload": "neu laden",
+  "web_app_crash.reload_page": "Die Seite {reload}",
+  "web_app_crash.report_issue": "Einen Fehler im {issuetracker} melden",
+  "web_app_crash.settings": "Einstellungen",
+  "web_app_crash.title": "Es tut uns leid, aber mit der Mastodon-App ist etwas schiefgelaufen."
+}
diff --git a/app/javascript/flavours/glitch/locales/defaultMessages.json b/app/javascript/flavours/glitch/locales/defaultMessages.json
new file mode 100644
index 000000000..d7aec67ac
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/defaultMessages.json
@@ -0,0 +1,1064 @@
+[
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "We're sorry, but something went wrong with the Mastodon app.",
+        "id": "web_app_crash.title"
+      },
+      {
+        "defaultMessage": "You could try any of the following:",
+        "id": "web_app_crash.content"
+      },
+      {
+        "defaultMessage": "Disable browser add-ons or built-in translation tools",
+        "id": "web_app_crash.disable_addons"
+      },
+      {
+        "defaultMessage": "Report a bug in the {issuetracker}",
+        "id": "web_app_crash.report_issue"
+      },
+      {
+        "defaultMessage": "issue tracker",
+        "id": "web_app_crash.issue_tracker"
+      },
+      {
+        "defaultMessage": "Debug information",
+        "id": "web_app_crash.debug_info"
+      },
+      {
+        "defaultMessage": "{reload} the current page",
+        "id": "web_app_crash.reload_page"
+      },
+      {
+        "defaultMessage": "Reload",
+        "id": "web_app_crash.reload"
+      },
+      {
+        "defaultMessage": "Change your {settings}",
+        "id": "web_app_crash.change_your_settings"
+      },
+      {
+        "defaultMessage": "settings",
+        "id": "web_app_crash.settings"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/components/error_boundary.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Sensitive",
+        "id": "media_gallery.sensitive"
+      },
+      {
+        "defaultMessage": "Click to view",
+        "id": "status.sensitive_toggle"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/components/media_gallery.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Select\nall",
+        "id": "notification_purge.btn_all"
+      },
+      {
+        "defaultMessage": "Select\nnone",
+        "id": "notification_purge.btn_none"
+      },
+      {
+        "defaultMessage": "Invert\nselection",
+        "id": "notification_purge.btn_invert"
+      },
+      {
+        "defaultMessage": "Clear\nselected",
+        "id": "notification_purge.btn_apply"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/components/notification_purge_buttons.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Collapse",
+        "id": "status.collapse"
+      },
+      {
+        "defaultMessage": "Uncollapse",
+        "id": "status.uncollapse"
+      },
+      {
+        "defaultMessage": "This toot is a reply",
+        "id": "status.in_reply_to"
+      },
+      {
+        "defaultMessage": "Features an attached preview card",
+        "id": "status.has_preview_card"
+      },
+      {
+        "defaultMessage": "Features attached pictures",
+        "id": "status.has_pictures"
+      },
+      {
+        "defaultMessage": "This toot is a poll",
+        "id": "status.is_poll"
+      },
+      {
+        "defaultMessage": "Features attached videos",
+        "id": "status.has_video"
+      },
+      {
+        "defaultMessage": "Features attached audio files",
+        "id": "status.has_audio"
+      },
+      {
+        "defaultMessage": "Only visible from your instance",
+        "id": "status.local_only"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/components/status_icons.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Are you sure you want to permanently clear all selected notifications?",
+        "id": "notifications.marked_clear_confirmation"
+      },
+      {
+        "defaultMessage": "Clear selected notifications",
+        "id": "notifications.marked_clear"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/containers/notification_purge_buttons_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Show",
+        "id": "confirmations.unfilter.confirm"
+      },
+      {
+        "defaultMessage": "Author",
+        "id": "confirmations.unfilter.author"
+      },
+      {
+        "defaultMessage": "Matching {count, plural, one {filter} other {filters}}",
+        "id": "confirmations.unfilter.filters"
+      },
+      {
+        "defaultMessage": "Edit filter",
+        "id": "confirmations.unfilter.edit_filter"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/containers/status_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Glitch-soc is free open source software forked from Mastodon.",
+        "id": "about.fork_disclaimer"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/about/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "No comment provided",
+        "id": "account_note.glitch_placeholder"
+      },
+      {
+        "defaultMessage": "Cancel",
+        "id": "account_note.cancel"
+      },
+      {
+        "defaultMessage": "Save",
+        "id": "account_note.save"
+      },
+      {
+        "defaultMessage": "Edit",
+        "id": "account_note.edit"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/account/components/account_note.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "This user has been suspended by a moderator.",
+        "id": "account.suspended_disclaimer_full"
+      },
+      {
+        "defaultMessage": "Information below may reflect the user's profile incompletely.",
+        "id": "account.disclaimer_full"
+      },
+      {
+        "defaultMessage": "View full profile",
+        "id": "account.view_full_profile"
+      },
+      {
+        "defaultMessage": "Follows",
+        "id": "account.follows"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/account/components/action_bar.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Add note for @{name}",
+        "id": "account.add_account_note"
+      },
+      {
+        "defaultMessage": "Joined {date}",
+        "id": "account.joined"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/account/components/header.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Profile",
+        "id": "column_header.profile"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/account/components/profile_column_header.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Filter out by regular expressions",
+        "id": "home.column_settings.filter_regex"
+      },
+      {
+        "defaultMessage": "Column settings",
+        "id": "home.settings"
+      },
+      {
+        "defaultMessage": "Advanced",
+        "id": "home.column_settings.advanced"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/community_timeline/components/column_settings.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+        "id": "confirmations.missing_media_description.message"
+      },
+      {
+        "defaultMessage": "Send anyway",
+        "id": "confirmations.missing_media_description.confirm"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/components/compose_form.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "App settings",
+        "id": "navigation_bar.app_settings"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/components/header.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Advanced options",
+        "id": "advanced_options.icon_title"
+      },
+      {
+        "defaultMessage": "Attach...",
+        "id": "compose.attach"
+      },
+      {
+        "defaultMessage": "Content type",
+        "id": "content-type.change"
+      },
+      {
+        "defaultMessage": "Draw something",
+        "id": "compose.attach.doodle"
+      },
+      {
+        "defaultMessage": "HTML",
+        "id": "compose.content-type.html"
+      },
+      {
+        "defaultMessage": "Do not post to other instances",
+        "id": "advanced_options.local-only.long"
+      },
+      {
+        "defaultMessage": "Local-only",
+        "id": "advanced_options.local-only.short"
+      },
+      {
+        "defaultMessage": "Markdown",
+        "id": "compose.content-type.markdown"
+      },
+      {
+        "defaultMessage": "Plain text",
+        "id": "compose.content-type.plain"
+      },
+      {
+        "defaultMessage": "Hide text behind warning",
+        "id": "compose_form.spoiler"
+      },
+      {
+        "defaultMessage": "Automatically opens a reply on posting",
+        "id": "advanced_options.threaded_mode.long"
+      },
+      {
+        "defaultMessage": "Threaded mode",
+        "id": "advanced_options.threaded_mode.short"
+      },
+      {
+        "defaultMessage": "Upload a file",
+        "id": "compose.attach.upload"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/components/options.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Allow one choice",
+        "id": "compose_form.poll.single_choice"
+      },
+      {
+        "defaultMessage": "Allow multiple choices",
+        "id": "compose_form.poll.multiple_choices"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/components/poll_form.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "This post is local-only",
+        "id": "advanced_options.local-only.tooltip"
+      },
+      {
+        "defaultMessage": "Threaded mode enabled",
+        "id": "advanced_options.threaded_mode.tooltip"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/components/textarea_icons.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+        "id": "confirmations.missing_media_description.message"
+      },
+      {
+        "defaultMessage": "Send anyway",
+        "id": "confirmations.missing_media_description.confirm"
+      },
+      {
+        "defaultMessage": "Edit media",
+        "id": "confirmations.missing_media_description.edit"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/containers/compose_form_container.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Filter out by regular expressions",
+        "id": "home.column_settings.filter_regex"
+      },
+      {
+        "defaultMessage": "Column settings",
+        "id": "home.settings"
+      },
+      {
+        "defaultMessage": "Group by conversation",
+        "id": "direct.group_by_conversations"
+      },
+      {
+        "defaultMessage": "Advanced",
+        "id": "home.column_settings.advanced"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Favourited by",
+        "id": "column.favourited_by"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/favourites/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Misc",
+        "id": "column.heading"
+      },
+      {
+        "defaultMessage": "Miscellaneous options",
+        "id": "column.subheading"
+      },
+      {
+        "defaultMessage": "Extended information",
+        "id": "navigation_bar.info"
+      },
+      {
+        "defaultMessage": "Show me around",
+        "id": "getting_started.onboarding"
+      },
+      {
+        "defaultMessage": "Keyboard shortcuts",
+        "id": "navigation_bar.keyboard_shortcuts"
+      },
+      {
+        "defaultMessage": "Featured users",
+        "id": "navigation_bar.featured_users"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/getting_started_misc/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Navigation",
+        "id": "column_subheading.navigation"
+      },
+      {
+        "defaultMessage": "App settings",
+        "id": "navigation_bar.app_settings"
+      },
+      {
+        "defaultMessage": "Keyboard shortcuts",
+        "id": "navigation_bar.keyboard_shortcuts"
+      },
+      {
+        "defaultMessage": "Lists",
+        "id": "column_subheading.lists"
+      },
+      {
+        "defaultMessage": "Misc",
+        "id": "navigation_bar.misc"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/getting_started/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Filter out by regular expressions",
+        "id": "home.column_settings.filter_regex"
+      },
+      {
+        "defaultMessage": "Column settings",
+        "id": "home.settings"
+      },
+      {
+        "defaultMessage": "Show DMs",
+        "id": "home.column_settings.show_direct"
+      },
+      {
+        "defaultMessage": "Advanced",
+        "id": "home.column_settings.advanced"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/home_timeline/components/column_settings.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "to bookmark",
+        "id": "keyboard_shortcuts.bookmark"
+      },
+      {
+        "defaultMessage": "to collapse/uncollapse toots",
+        "id": "keyboard_shortcuts.toggle_collapse"
+      },
+      {
+        "defaultMessage": "to send toot using secondary privacy setting",
+        "id": "keyboard_shortcuts.secondary_toot"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/keyboard_shortcuts/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "General",
+        "id": "settings.general"
+      },
+      {
+        "defaultMessage": "Compose box",
+        "id": "settings.compose_box_opts"
+      },
+      {
+        "defaultMessage": "Content Warnings",
+        "id": "settings.content_warnings"
+      },
+      {
+        "defaultMessage": "Collapsed toots",
+        "id": "settings.collapsed_statuses"
+      },
+      {
+        "defaultMessage": "Media",
+        "id": "settings.media"
+      },
+      {
+        "defaultMessage": "Preferences",
+        "id": "settings.preferences"
+      },
+      {
+        "defaultMessage": "Close",
+        "id": "settings.close"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/local_settings/navigation/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Auto",
+        "id": "layout.auto"
+      },
+      {
+        "defaultMessage": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+        "id": "layout.hint.auto"
+      },
+      {
+        "defaultMessage": "Desktop",
+        "id": "layout.desktop"
+      },
+      {
+        "defaultMessage": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+        "id": "layout.hint.desktop"
+      },
+      {
+        "defaultMessage": "Mobile",
+        "id": "layout.single"
+      },
+      {
+        "defaultMessage": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+        "id": "layout.hint.single"
+      },
+      {
+        "defaultMessage": "None",
+        "id": "settings.side_arm.none"
+      },
+      {
+        "defaultMessage": "Keep its set privacy",
+        "id": "settings.side_arm_reply_mode.keep"
+      },
+      {
+        "defaultMessage": "Copy privacy setting of the toot being replied to",
+        "id": "settings.side_arm_reply_mode.copy"
+      },
+      {
+        "defaultMessage": "Restrict privacy setting to that of the toot being replied to",
+        "id": "settings.side_arm_reply_mode.restrict"
+      },
+      {
+        "defaultMessage": "Regular expression",
+        "id": "settings.content_warnings.regexp"
+      },
+      {
+        "defaultMessage": "Do not rewrite mentions",
+        "id": "settings.rewrite_mentions_no"
+      },
+      {
+        "defaultMessage": "Rewrite with username and domain (when the account is remote)",
+        "id": "settings.rewrite_mentions_acct"
+      },
+      {
+        "defaultMessage": "Rewrite with username",
+        "id": "settings.rewrite_mentions_username"
+      },
+      {
+        "defaultMessage": "Left",
+        "id": "settings.pop_in_left"
+      },
+      {
+        "defaultMessage": "Right",
+        "id": "settings.pop_in_right"
+      },
+      {
+        "defaultMessage": "General",
+        "id": "settings.general"
+      },
+      {
+        "defaultMessage": "Display an estimate of the reply count",
+        "id": "settings.show_reply_counter"
+      },
+      {
+        "defaultMessage": "High color privacy icons",
+        "id": "settings.hicolor_privacy_icons"
+      },
+      {
+        "defaultMessage": "Display privacy icons in bright and easily distinguishable colors",
+        "id": "settings.hicolor_privacy_icons.hint"
+      },
+      {
+        "defaultMessage": "Show confirmation dialog before boosting toots lacking media descriptions",
+        "id": "settings.confirm_boost_missing_media_description"
+      },
+      {
+        "defaultMessage": "Tag misleading links",
+        "id": "settings.tag_misleading_links"
+      },
+      {
+        "defaultMessage": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+        "id": "settings.tag_misleading_links.hint"
+      },
+      {
+        "defaultMessage": "Rewrite mentions in displayed statuses",
+        "id": "settings.rewrite_mentions"
+      },
+      {
+        "defaultMessage": "Notifications options",
+        "id": "settings.notifications_opts"
+      },
+      {
+        "defaultMessage": "Unread notifications badge",
+        "id": "settings.notifications.tab_badge"
+      },
+      {
+        "defaultMessage": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+        "id": "settings.notifications.tab_badge.hint"
+      },
+      {
+        "defaultMessage": "Unread notifications favicon badge",
+        "id": "settings.notifications.favicon_badge"
+      },
+      {
+        "defaultMessage": "Add a badge for unread notifications to the favicon",
+        "id": "settings.notifications.favicon_badge.hint"
+      },
+      {
+        "defaultMessage": "Toot icons",
+        "id": "settings.status_icons"
+      },
+      {
+        "defaultMessage": "Language indicator",
+        "id": "settings.status_icons_language"
+      },
+      {
+        "defaultMessage": "Reply indicator",
+        "id": "settings.status_icons_reply"
+      },
+      {
+        "defaultMessage": "Local-only indicator",
+        "id": "settings.status_icons_local_only"
+      },
+      {
+        "defaultMessage": "Media and poll indicators",
+        "id": "settings.status_icons_media"
+      },
+      {
+        "defaultMessage": "Toot privacy indicator",
+        "id": "settings.status_icons_visibility"
+      },
+      {
+        "defaultMessage": "Layout options",
+        "id": "settings.layout_opts"
+      },
+      {
+        "defaultMessage": "Layout:",
+        "id": "settings.layout"
+      },
+      {
+        "defaultMessage": "Wide view (Desktop mode only)",
+        "id": "settings.wide_view"
+      },
+      {
+        "defaultMessage": "Stretches columns to better fill the available space.",
+        "id": "settings.wide_view_hint"
+      },
+      {
+        "defaultMessage": "Compose box",
+        "id": "settings.compose_box_opts"
+      },
+      {
+        "defaultMessage": "Always enable the Content Warning field",
+        "id": "settings.always_show_spoilers_field"
+      },
+      {
+        "defaultMessage": "Prepend “re: ” to content warnings when replying",
+        "id": "settings.prepend_cw_re"
+      },
+      {
+        "defaultMessage": "Pre-select usernames on reply",
+        "id": "settings.preselect_on_reply"
+      },
+      {
+        "defaultMessage": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+        "id": "settings.preselect_on_reply_hint"
+      },
+      {
+        "defaultMessage": "Show confirmation dialog before sending toots lacking media descriptions",
+        "id": "settings.confirm_missing_media_description"
+      },
+      {
+        "defaultMessage": "Show confirmation dialog before overwriting the message being composed",
+        "id": "settings.confirm_before_clearing_draft"
+      },
+      {
+        "defaultMessage": "Show content-type choice when authoring toots",
+        "id": "settings.show_content_type_choice"
+      },
+      {
+        "defaultMessage": "Secondary toot button:",
+        "id": "settings.side_arm"
+      },
+      {
+        "defaultMessage": "When replying to a toot, the secondary toot button should:",
+        "id": "settings.side_arm_reply_mode"
+      },
+      {
+        "defaultMessage": "Content warnings",
+        "id": "settings.content_warnings"
+      },
+      {
+        "defaultMessage": "Show/hide content of all copies at once",
+        "id": "settings.content_warnings_shared_state"
+      },
+      {
+        "defaultMessage": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+        "id": "settings.content_warnings_shared_state_hint"
+      },
+      {
+        "defaultMessage": "Display media attachments outside content warnings",
+        "id": "settings.content_warnings_media_outside"
+      },
+      {
+        "defaultMessage": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+        "id": "settings.content_warnings_media_outside_hint"
+      },
+      {
+        "defaultMessage": "Auto-unfolding options",
+        "id": "settings.content_warnings_unfold_opts"
+      },
+      {
+        "defaultMessage": "Automatically unfold content-warnings",
+        "id": "settings.enable_content_warnings_auto_unfold"
+      },
+      {
+        "defaultMessage": "This setting is now controlled from Mastodon's {settings_page_link}",
+        "id": "settings.deprecated_setting"
+      },
+      {
+        "defaultMessage": "user preferences",
+        "id": "settings.shared_settings_link"
+      },
+      {
+        "defaultMessage": "Content warnings to not automatically unfold:",
+        "id": "settings.content_warnings_filter"
+      },
+      {
+        "defaultMessage": "Collapsed toots",
+        "id": "settings.collapsed_statuses"
+      },
+      {
+        "defaultMessage": "Enable collapsed toots",
+        "id": "settings.enable_collapsed"
+      },
+      {
+        "defaultMessage": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+        "id": "settings.enable_collapsed_hint"
+      },
+      {
+        "defaultMessage": "Show action buttons in collapsed toots",
+        "id": "settings.show_action_bar"
+      },
+      {
+        "defaultMessage": "Automatic collapsing",
+        "id": "settings.auto_collapse"
+      },
+      {
+        "defaultMessage": "Everything",
+        "id": "settings.auto_collapse_all"
+      },
+      {
+        "defaultMessage": "Notifications",
+        "id": "settings.auto_collapse_notifications"
+      },
+      {
+        "defaultMessage": "Lengthy toots",
+        "id": "settings.auto_collapse_lengthy"
+      },
+      {
+        "defaultMessage": "Boosts",
+        "id": "settings.auto_collapse_reblogs"
+      },
+      {
+        "defaultMessage": "Replies",
+        "id": "settings.auto_collapse_replies"
+      },
+      {
+        "defaultMessage": "Toots with media",
+        "id": "settings.auto_collapse_media"
+      },
+      {
+        "defaultMessage": "Image backgrounds",
+        "id": "settings.image_backgrounds"
+      },
+      {
+        "defaultMessage": "Give collapsed toots an image background",
+        "id": "settings.image_backgrounds_users"
+      },
+      {
+        "defaultMessage": "Preview collapsed toot media",
+        "id": "settings.image_backgrounds_media"
+      },
+      {
+        "defaultMessage": "If the post has any media attachment, use the first one as a background",
+        "id": "settings.image_backgrounds_media_hint"
+      },
+      {
+        "defaultMessage": "Media",
+        "id": "settings.media"
+      },
+      {
+        "defaultMessage": "Letterbox media",
+        "id": "settings.media_letterbox"
+      },
+      {
+        "defaultMessage": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+        "id": "settings.media_letterbox_hint"
+      },
+      {
+        "defaultMessage": "Full-width media previews",
+        "id": "settings.media_fullwidth"
+      },
+      {
+        "defaultMessage": "Inline preview cards for external links",
+        "id": "settings.inline_preview_cards"
+      },
+      {
+        "defaultMessage": "Reveal sensitive media behind a CW by default",
+        "id": "settings.media_reveal_behind_cw"
+      },
+      {
+        "defaultMessage": "Enable pop-in player",
+        "id": "settings.pop_in_player"
+      },
+      {
+        "defaultMessage": "Pop-in player position:",
+        "id": "settings.pop_in_position"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/local_settings/page/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Mark for deletion",
+        "id": "notification.markForDeletion"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/notifications/components/overlay.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Enter notification cleaning mode",
+        "id": "notification_purge.start"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/notifications/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Featured accounts",
+        "id": "endorsed_accounts_editor.endorsed_accounts"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/pinned_accounts_editor/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Filter out by regular expressions",
+        "id": "home.column_settings.filter_regex"
+      },
+      {
+        "defaultMessage": "Show local-only toots",
+        "id": "community.column_settings.allow_local_only"
+      },
+      {
+        "defaultMessage": "Advanced",
+        "id": "home.column_settings.advanced"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/public_timeline/components/column_settings.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Boosted by",
+        "id": "column.reblogged_by"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/reblogs/index.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "This toot contains some media without description",
+        "id": "boost_modal.missing_description"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/components/boost_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Do not ask for confirmation again",
+        "id": "confirmation_modal.do_not_ask_again"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/components/confirmation_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Use Mastodon preferences",
+        "id": "confirmations.deprecated_settings.confirm"
+      },
+      {
+        "defaultMessage": "Automatically unfold content-warnings",
+        "id": "settings.enable_content_warnings_auto_unfold"
+      },
+      {
+        "defaultMessage": "Allow swiping to change columns (Mobile only)",
+        "id": "settings.swipe_to_change_columns"
+      },
+      {
+        "defaultMessage": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+        "id": "confirmations.deprecated_settings.message"
+      },
+      {
+        "defaultMessage": "App settings",
+        "id": "navigation_bar.app_settings"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "You can press {combo} to skip this next time",
+        "id": "favourite_modal.combo"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/components/favourite_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "App settings",
+        "id": "navigation_bar.app_settings"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/components/navigation_panel.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "Welcome to {domain}!",
+        "id": "onboarding.page_one.welcome"
+      },
+      {
+        "defaultMessage": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+        "id": "onboarding.page_one.federation"
+      },
+      {
+        "defaultMessage": "You are on {domain}, so your full handle is {handle}",
+        "id": "onboarding.page_one.handle"
+      },
+      {
+        "defaultMessage": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.",
+        "id": "onboarding.page_two.compose"
+      },
+      {
+        "defaultMessage": "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.",
+        "id": "onboarding.page_three.search"
+      },
+      {
+        "defaultMessage": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.",
+        "id": "onboarding.page_three.profile"
+      },
+      {
+        "defaultMessage": "The home timeline shows posts from people you follow.",
+        "id": "onboarding.page_four.home"
+      },
+      {
+        "defaultMessage": "The notifications column shows when someone interacts with you.",
+        "id": "onboarding.page_four.notifications"
+      },
+      {
+        "defaultMessage": "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.",
+        "id": "onboarding.page_five.public_timelines"
+      },
+      {
+        "defaultMessage": "Your instance's admin is {admin}.",
+        "id": "onboarding.page_six.admin"
+      },
+      {
+        "defaultMessage": "Please read {domain}'s {guidelines}!",
+        "id": "onboarding.page_six.read_guidelines"
+      },
+      {
+        "defaultMessage": "community guidelines",
+        "id": "onboarding.page_six.guidelines"
+      },
+      {
+        "defaultMessage": "Almost done...",
+        "id": "onboarding.page_six.almost_done"
+      },
+      {
+        "defaultMessage": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+        "id": "onboarding.page_six.github"
+      },
+      {
+        "defaultMessage": "There are {apps} available for iOS, Android and other platforms.",
+        "id": "onboarding.page_six.apps_available"
+      },
+      {
+        "defaultMessage": "mobile apps",
+        "id": "onboarding.page_six.various_app"
+      },
+      {
+        "defaultMessage": "Bon Appetoot!",
+        "id": "onboarding.page_six.appetoot"
+      },
+      {
+        "defaultMessage": "Next",
+        "id": "onboarding.next"
+      },
+      {
+        "defaultMessage": "Done",
+        "id": "onboarding.done"
+      },
+      {
+        "defaultMessage": "Skip",
+        "id": "onboarding.skip"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/components/onboarding_modal.json"
+  },
+  {
+    "descriptors": [
+      {
+        "defaultMessage": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+        "id": "moved_to_warning"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/ui/index.json"
+  }
+]
\ No newline at end of file
diff --git a/app/javascript/flavours/glitch/locales/el.js b/app/javascript/flavours/glitch/locales/el.js
deleted file mode 100644
index 2d9bb829f..000000000
--- a/app/javascript/flavours/glitch/locales/el.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/el.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/el.json b/app/javascript/flavours/glitch/locales/el.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/el.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/en-GB.json b/app/javascript/flavours/glitch/locales/en-GB.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/en-GB.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/en.js b/app/javascript/flavours/glitch/locales/en.js
deleted file mode 100644
index 90e924d4a..000000000
--- a/app/javascript/flavours/glitch/locales/en.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import inherited from 'mastodon/locales/en.json';
-
-const messages = {
-  'getting_started.open_source_notice': 'Glitchsoc is free open source software forked from {Mastodon}. You can contribute or report issues on GitHub at {github}.',
-  'layout.auto': 'Auto',
-  'layout.current_is': 'Your current layout is:',
-  'layout.desktop': 'Desktop',
-  'layout.mobile': 'Mobile',
-  'navigation_bar.app_settings': 'App settings',
-  'getting_started.onboarding': 'Show me around',
-  'onboarding.page_one.federation': '{domain} is an \'instance\' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.',
-  'onboarding.page_one.welcome': 'Welcome to {domain}!',
-  'onboarding.page_six.github': '{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.',
-  'settings.auto_collapse': 'Automatic collapsing',
-  'settings.auto_collapse_all': 'Everything',
-  'settings.auto_collapse_lengthy': 'Lengthy toots',
-  'settings.auto_collapse_media': 'Toots with media',
-  'settings.auto_collapse_notifications': 'Notifications',
-  'settings.auto_collapse_reblogs': 'Boosts',
-  'settings.auto_collapse_replies': 'Replies',
-  'settings.show_action_bar': 'Show action buttons in collapsed toots',
-  'settings.close': 'Close',
-  'settings.collapsed_statuses': 'Collapsed toots',
-  'settings.enable_collapsed': 'Enable collapsed toots',
-  'settings.general': 'General',
-  'settings.image_backgrounds': 'Image backgrounds',
-  'settings.image_backgrounds_media': 'Preview collapsed toot media',
-  'settings.image_backgrounds_users': 'Give collapsed toots an image background',
-  'settings.media': 'Media',
-  'settings.media_letterbox': 'Letterbox media',
-  'settings.media_fullwidth': 'Full-width media previews',
-  'settings.preferences': 'User preferences',
-  'settings.wide_view': 'Wide view (Desktop mode only)',
-  'settings.navbar_under': 'Navbar at the bottom (Mobile only)',
-  'status.collapse': 'Collapse',
-  'status.uncollapse': 'Uncollapse',
-
-  'media_gallery.sensitive': 'Sensitive',
-
-  'favourite_modal.combo': 'You can press {combo} to skip this next time',
-
-  'home.column_settings.show_direct': 'Show DMs',
-
-  'notification.markForDeletion': 'Mark for deletion',
-  'notifications.clear': 'Clear all my notifications',
-  'notifications.marked_clear_confirmation': 'Are you sure you want to permanently clear all selected notifications?',
-  'notifications.marked_clear': 'Clear selected notifications',
-
-  'notification_purge.btn_all': 'Select\nall',
-  'notification_purge.btn_none': 'Select\nnone',
-  'notification_purge.btn_invert': 'Invert\nselection',
-  'notification_purge.btn_apply': 'Clear\nselected',
-
-  'compose.attach.upload': 'Upload a file',
-  'compose.attach.doodle': 'Draw something',
-  'compose.attach': 'Attach...',
-
-  'advanced_options.local-only.short': 'Local-only',
-  'advanced_options.local-only.long': 'Do not post to other instances',
-  'advanced_options.local-only.tooltip': 'This post is local-only',
-  'advanced_options.icon_title': 'Advanced options',
-  'advanced_options.threaded_mode.short': 'Threaded mode',
-  'advanced_options.threaded_mode.long': 'Automatically opens a reply on posting',
-  'advanced_options.threaded_mode.tooltip': 'Threaded mode enabled',
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/en.json b/app/javascript/flavours/glitch/locales/en.json
new file mode 100644
index 000000000..59f2f74b1
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/en.json
@@ -0,0 +1,200 @@
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?",
+  "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": "{domain} is an 'instance' of Mastodon. 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 {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. 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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content Warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "User preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/eo.js b/app/javascript/flavours/glitch/locales/eo.js
deleted file mode 100644
index 04192f506..000000000
--- a/app/javascript/flavours/glitch/locales/eo.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/eo.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/eo.json b/app/javascript/flavours/glitch/locales/eo.json
new file mode 100644
index 000000000..87fe6c657
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/eo.json
@@ -0,0 +1,52 @@
+{
+  "account.add_account_note": "Aldoni noton por @{name}",
+  "account_note.cancel": "Nuligi",
+  "account_note.edit": "Redakti",
+  "account_note.save": "Konservi",
+  "column.reblogged_by": "Diskonigita de",
+  "column.subheading": "Diversaj agordoj",
+  "column_header.profile": "Profilo",
+  "column_subheading.lists": "Listoj",
+  "compose.attach": "Aldoni…",
+  "compose.attach.doodle": "Desegni ion",
+  "compose.attach.upload": "Alŝuti dosieron",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "confirmations.unfilter.author": "Aŭtoro",
+  "confirmations.unfilter.confirm": "Montri",
+  "confirmations.unfilter.edit_filter": "Redakti filtrilon",
+  "navigation_bar.keyboard_shortcuts": "Fulmoklavoj",
+  "notification_purge.btn_all": "Selekti ĉiujn",
+  "notification_purge.btn_apply": "Forigi selektajn",
+  "notification_purge.btn_invert": "Inverti selekton",
+  "notification_purge.btn_none": "Elekti neniun",
+  "notifications.marked_clear": "Forigi selektajn sciigojn",
+  "onboarding.next": "Sekva",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.almost_done": "Preskaŭ finita…",
+  "onboarding.page_six.apps_available": "Estas {apps} disponeblaj por iOS, Android kaj aliaj sistemoj.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.various_app": "poŝtelefonaj aplikaĵoj",
+  "settings.auto_collapse_all": "Ĉiuj",
+  "settings.auto_collapse_lengthy": "Longaj afiŝoj",
+  "settings.auto_collapse_media": "Afiŝoj kun aŭdovidaĵoj",
+  "settings.auto_collapse_notifications": "Sciigoj",
+  "settings.auto_collapse_reblogs": "Diskonigoj",
+  "settings.auto_collapse_replies": "Respondoj",
+  "settings.close": "Fermi",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regula esprimo",
+  "settings.preferences": "Preferences",
+  "settings.shared_settings_link": "preferoj de uzanto",
+  "settings.side_arm": "Duaranga butono por afiŝi:",
+  "settings.side_arm.none": "Neniu",
+  "settings.status_icons": "Ikonoj sur la afiŝoj",
+  "settings.status_icons_language": "Indikilo de lingvo",
+  "settings.status_icons_media": "Indikilo de aŭdovidaĵojn kaj balotenketo",
+  "settings.status_icons_reply": "Indikilo de respondoj",
+  "settings.status_icons_visibility": "Indikilo de privateco de afiŝo",
+  "web_app_crash.change_your_settings": "Ŝanĝi viajn {settings}",
+  "web_app_crash.reload": "Reŝarĝi",
+  "web_app_crash.reload_page": "{reload} la nunan paĝon",
+  "web_app_crash.settings": "agordojn"
+}
diff --git a/app/javascript/flavours/glitch/locales/es-AR.js b/app/javascript/flavours/glitch/locales/es-AR.js
deleted file mode 100644
index 0dffabcd4..000000000
--- a/app/javascript/flavours/glitch/locales/es-AR.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/es-AR.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/es-AR.json b/app/javascript/flavours/glitch/locales/es-AR.json
new file mode 100644
index 000000000..fc5a2e904
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/es-AR.json
@@ -0,0 +1,95 @@
+{
+  "advanced_options.icon_title": "Opciones avanzadas",
+  "advanced_options.local-only.long": "No publicar a otras instancias",
+  "advanced_options.local-only.short": "Local",
+  "advanced_options.local-only.tooltip": "Este toot es local",
+  "advanced_options.threaded_mode.long": "Al publicar abre automáticamente una respuesta",
+  "advanced_options.threaded_mode.short": "Modo hilo",
+  "advanced_options.threaded_mode.tooltip": "Modo hilo habilitado",
+  "compose.attach": "Adjuntar...",
+  "compose.attach.doodle": "Dibujar algo",
+  "compose.attach.upload": "Subir un archivo",
+  "confirmations.unfilter.author": "Publicado por",
+  "confirmations.unfilter.confirm": "Mostrar",
+  "confirmations.unfilter.edit_filter": "Editar filtro",
+  "confirmations.unfilter.filters": "Coincidencia con {count, plural, one {filtro} other {filtros}}",
+  "favourite_modal.combo": "Puedes presionar {combo} para omitir esto la próxima vez",
+  "getting_started.onboarding": "Paseo inicial",
+  "home.column_settings.show_direct": "Mostrar mensajes directos",
+  "layout.auto": "Automático",
+  "layout.desktop": "Escritorio",
+  "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y tamaño de pantalla",
+  "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "media_gallery.sensitive": "Sensible",
+  "navigation_bar.app_settings": "Ajustes de aplicación",
+  "notification.markForDeletion": "Marcar para borrar",
+  "notification_purge.btn_all": "Seleccionar\ntodo",
+  "notification_purge.btn_apply": "Borrar\nselección",
+  "notification_purge.btn_invert": "Invertir\nselección",
+  "notification_purge.btn_none": "Seleccionar\nnada",
+  "notifications.marked_clear": "Limpiar notificaciones seleccionadas",
+  "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.welcome": "¡Bienvenidx a {domain}!",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
+  "settings.auto_collapse": "Colapsar automáticamente",
+  "settings.auto_collapse_all": "Todo",
+  "settings.auto_collapse_lengthy": "Toots largos",
+  "settings.auto_collapse_media": "Toots con medios",
+  "settings.auto_collapse_notifications": "Notificaciones",
+  "settings.auto_collapse_reblogs": "Retoots",
+  "settings.auto_collapse_replies": "Respuestas",
+  "settings.close": "Cerrar",
+  "settings.collapsed_statuses": "Toots colapsados",
+  "settings.compose_box_opts": "Cuadro de redacción",
+  "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmación antes de sobreescribir un mensaje estabas escribiendo",
+  "settings.confirm_boost_missing_media_description": "Mostrar diálogo de confirmación antes de retootear publicaciones con medios sin descripción",
+  "settings.confirm_missing_media_description": "Mostrar diálogo de confirmación antes de publicar toots con medios sin descripción",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regexp (expresión regular)",
+  "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:",
+  "settings.enable_collapsed": "Habilitar toots colapsados",
+  "settings.enable_content_warnings_auto_unfold": "Descolapsar automáticamente advertencias de contenido",
+  "settings.hicolor_privacy_icons": "Íconos de privacidad más visibles",
+  "settings.image_backgrounds": "Fondos de imágenes",
+  "settings.image_backgrounds_media": "Vista previa de medios de toots colapsados",
+  "settings.image_backgrounds_users": "Darle fondo de imagen a toots colapsados",
+  "settings.inline_preview_cards": "Vista previa para enlaces externos",
+  "settings.layout": "Diseño",
+  "settings.layout_opts": "Opciones de diseño",
+  "settings.media": "Medios",
+  "settings.media_fullwidth": "Ancho completo al mostrar medios ",
+  "settings.media_letterbox": "Mantener proporciones al mostrar medios",
+  "settings.media_letterbox_hint": "Escalar medios para que llenen el espacio del contenedor sin cambiar sus proporciones sin recortarlos",
+  "settings.media_reveal_behind_cw": "Siempre mostrar medios sensibles dentro de las advertencias de contenido",
+  "settings.notifications.favicon_badge": "Marcador de notificaciones en el favicon",
+  "settings.notifications.favicon_badge.hint": "Muestra un marcador de notificaciones sin leer en el favicon",
+  "settings.notifications.tab_badge": "Marcador de notificaciones no leídas",
+  "settings.notifications.tab_badge.hint": "Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta",
+  "settings.notifications_opts": "Opciones de notificaciones",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Anteponer \"re: \" a las advertencias de contenido al responder",
+  "settings.preselect_on_reply": "Preseleccionar nombres de usuarix al responder",
+  "settings.preselect_on_reply_hint": "Al responder a conversaciones con múltiples participantes, preselecciona los nombres de usuarix subsecuentes del/la primerx",
+  "settings.rewrite_mentions": "Reescribir menciones in publicaciones mostradas",
+  "settings.rewrite_mentions_acct": "Reescribir con nombre de usuarix y dominio (para cuentas remotas)",
+  "settings.rewrite_mentions_no": "No reescribir menciones",
+  "settings.rewrite_mentions_username": "Reescribir con nombre de usuarix",
+  "settings.show_action_bar": "Mostrar botones de acción en toots colapsados",
+  "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear toots",
+  "settings.show_reply_counter": "Mostrar un conteo estimado de respuestas",
+  "settings.side_arm": "Botón secundario:",
+  "settings.side_arm.none": "Ninguno",
+  "settings.side_arm_reply_mode": "Al responder a un toot, el botón de toot secundario debe:",
+  "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad del toot al que estás respondiendo",
+  "settings.side_arm_reply_mode.keep": "Conservar opción de privacidad",
+  "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma del toot al que estás respondiendo",
+  "settings.swipe_to_change_columns": "Permitir deslizar para cambiar columnas (Sólo en móvil)",
+  "settings.tag_misleading_links": "Marcar enlaces engañosos",
+  "settings.tag_misleading_links.hint": "Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente",
+  "settings.wide_view": "Vista amplia (solo modo de escritorio)",
+  "status.collapse": "Colapsar",
+  "status.uncollapse": "Descolapsar"
+}
diff --git a/app/javascript/flavours/glitch/locales/es-MX.js b/app/javascript/flavours/glitch/locales/es-MX.js
deleted file mode 100644
index eaefa20ef..000000000
--- a/app/javascript/flavours/glitch/locales/es-MX.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/es-MX.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/es-MX.json b/app/javascript/flavours/glitch/locales/es-MX.json
new file mode 100644
index 000000000..fc5a2e904
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/es-MX.json
@@ -0,0 +1,95 @@
+{
+  "advanced_options.icon_title": "Opciones avanzadas",
+  "advanced_options.local-only.long": "No publicar a otras instancias",
+  "advanced_options.local-only.short": "Local",
+  "advanced_options.local-only.tooltip": "Este toot es local",
+  "advanced_options.threaded_mode.long": "Al publicar abre automáticamente una respuesta",
+  "advanced_options.threaded_mode.short": "Modo hilo",
+  "advanced_options.threaded_mode.tooltip": "Modo hilo habilitado",
+  "compose.attach": "Adjuntar...",
+  "compose.attach.doodle": "Dibujar algo",
+  "compose.attach.upload": "Subir un archivo",
+  "confirmations.unfilter.author": "Publicado por",
+  "confirmations.unfilter.confirm": "Mostrar",
+  "confirmations.unfilter.edit_filter": "Editar filtro",
+  "confirmations.unfilter.filters": "Coincidencia con {count, plural, one {filtro} other {filtros}}",
+  "favourite_modal.combo": "Puedes presionar {combo} para omitir esto la próxima vez",
+  "getting_started.onboarding": "Paseo inicial",
+  "home.column_settings.show_direct": "Mostrar mensajes directos",
+  "layout.auto": "Automático",
+  "layout.desktop": "Escritorio",
+  "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y tamaño de pantalla",
+  "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "media_gallery.sensitive": "Sensible",
+  "navigation_bar.app_settings": "Ajustes de aplicación",
+  "notification.markForDeletion": "Marcar para borrar",
+  "notification_purge.btn_all": "Seleccionar\ntodo",
+  "notification_purge.btn_apply": "Borrar\nselección",
+  "notification_purge.btn_invert": "Invertir\nselección",
+  "notification_purge.btn_none": "Seleccionar\nnada",
+  "notifications.marked_clear": "Limpiar notificaciones seleccionadas",
+  "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.welcome": "¡Bienvenidx a {domain}!",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
+  "settings.auto_collapse": "Colapsar automáticamente",
+  "settings.auto_collapse_all": "Todo",
+  "settings.auto_collapse_lengthy": "Toots largos",
+  "settings.auto_collapse_media": "Toots con medios",
+  "settings.auto_collapse_notifications": "Notificaciones",
+  "settings.auto_collapse_reblogs": "Retoots",
+  "settings.auto_collapse_replies": "Respuestas",
+  "settings.close": "Cerrar",
+  "settings.collapsed_statuses": "Toots colapsados",
+  "settings.compose_box_opts": "Cuadro de redacción",
+  "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmación antes de sobreescribir un mensaje estabas escribiendo",
+  "settings.confirm_boost_missing_media_description": "Mostrar diálogo de confirmación antes de retootear publicaciones con medios sin descripción",
+  "settings.confirm_missing_media_description": "Mostrar diálogo de confirmación antes de publicar toots con medios sin descripción",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regexp (expresión regular)",
+  "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:",
+  "settings.enable_collapsed": "Habilitar toots colapsados",
+  "settings.enable_content_warnings_auto_unfold": "Descolapsar automáticamente advertencias de contenido",
+  "settings.hicolor_privacy_icons": "Íconos de privacidad más visibles",
+  "settings.image_backgrounds": "Fondos de imágenes",
+  "settings.image_backgrounds_media": "Vista previa de medios de toots colapsados",
+  "settings.image_backgrounds_users": "Darle fondo de imagen a toots colapsados",
+  "settings.inline_preview_cards": "Vista previa para enlaces externos",
+  "settings.layout": "Diseño",
+  "settings.layout_opts": "Opciones de diseño",
+  "settings.media": "Medios",
+  "settings.media_fullwidth": "Ancho completo al mostrar medios ",
+  "settings.media_letterbox": "Mantener proporciones al mostrar medios",
+  "settings.media_letterbox_hint": "Escalar medios para que llenen el espacio del contenedor sin cambiar sus proporciones sin recortarlos",
+  "settings.media_reveal_behind_cw": "Siempre mostrar medios sensibles dentro de las advertencias de contenido",
+  "settings.notifications.favicon_badge": "Marcador de notificaciones en el favicon",
+  "settings.notifications.favicon_badge.hint": "Muestra un marcador de notificaciones sin leer en el favicon",
+  "settings.notifications.tab_badge": "Marcador de notificaciones no leídas",
+  "settings.notifications.tab_badge.hint": "Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta",
+  "settings.notifications_opts": "Opciones de notificaciones",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Anteponer \"re: \" a las advertencias de contenido al responder",
+  "settings.preselect_on_reply": "Preseleccionar nombres de usuarix al responder",
+  "settings.preselect_on_reply_hint": "Al responder a conversaciones con múltiples participantes, preselecciona los nombres de usuarix subsecuentes del/la primerx",
+  "settings.rewrite_mentions": "Reescribir menciones in publicaciones mostradas",
+  "settings.rewrite_mentions_acct": "Reescribir con nombre de usuarix y dominio (para cuentas remotas)",
+  "settings.rewrite_mentions_no": "No reescribir menciones",
+  "settings.rewrite_mentions_username": "Reescribir con nombre de usuarix",
+  "settings.show_action_bar": "Mostrar botones de acción en toots colapsados",
+  "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear toots",
+  "settings.show_reply_counter": "Mostrar un conteo estimado de respuestas",
+  "settings.side_arm": "Botón secundario:",
+  "settings.side_arm.none": "Ninguno",
+  "settings.side_arm_reply_mode": "Al responder a un toot, el botón de toot secundario debe:",
+  "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad del toot al que estás respondiendo",
+  "settings.side_arm_reply_mode.keep": "Conservar opción de privacidad",
+  "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma del toot al que estás respondiendo",
+  "settings.swipe_to_change_columns": "Permitir deslizar para cambiar columnas (Sólo en móvil)",
+  "settings.tag_misleading_links": "Marcar enlaces engañosos",
+  "settings.tag_misleading_links.hint": "Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente",
+  "settings.wide_view": "Vista amplia (solo modo de escritorio)",
+  "status.collapse": "Colapsar",
+  "status.uncollapse": "Descolapsar"
+}
diff --git a/app/javascript/flavours/glitch/locales/es.js b/app/javascript/flavours/glitch/locales/es.js
deleted file mode 100644
index f22062977..000000000
--- a/app/javascript/flavours/glitch/locales/es.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import inherited from 'mastodon/locales/es.json';
-
-const messages = {
-  'advanced_options.icon_title': 'Opciones avanzadas',
-  'advanced_options.local-only.long': 'No publicar a otras instancias',
-  'advanced_options.local-only.short': 'Local',
-  'advanced_options.local-only.tooltip': 'Este toot es local',
-  'advanced_options.threaded_mode.long': 'Al publicar abre automáticamente una respuesta',
-  'advanced_options.threaded_mode.short': 'Modo hilo',
-  'advanced_options.threaded_mode.tooltip': 'Modo hilo habilitado',
-  'compose.attach.doodle': 'Dibujar algo',
-  'compose.attach.upload': 'Subir un archivo',
-  'compose.attach': 'Adjuntar...',
-  'favourite_modal.combo': 'Puedes presionar {combo} para omitir esto la próxima vez',
-  'getting_started.onboarding': 'Paseo inicial',
-  'getting_started.open_source_notice': 'Glitchsoc es software libre y gratuito bifurcado de {Mastodon}. Puedes contribuir o reportar errores en GitHub en {github}.',
-  'home.column_settings.show_direct': 'Mostrar mensajes directos',
-  'layout.auto': 'Automático',
-  'layout.current_is': 'Tu diseño actual es:',
-  'layout.desktop': 'Escritorio',
-  'layout.hint.auto': 'Seleccionar un diseño automáticamente basado en "Habilitar interface web avanzada" y tamaño de pantalla',
-  'layout.hint.desktop': 'Utiliza el diseño multi-columna sin importar "Habilitar interface web avanzada" o tamaño de pantalla',
-  'layout.hint.single': 'Utiliza el diseño de una columna sin importar "Habilitar interface web avanzada" o tamaño de pantalla',
-  'layout.mobile': 'Móvil',
-  'media_gallery.sensitive': 'Sensible',
-  'navigation_bar.app_settings': 'Ajustes de aplicación',
-  'notification_purge.btn_all': 'Seleccionar\ntodo',
-  'notification_purge.btn_apply': 'Borrar\nselección',
-  'notification_purge.btn_invert': 'Invertir\nselección',
-  'notification_purge.btn_none': 'Seleccionar\nnada',
-  'notification.markForDeletion': 'Marcar para borrar',
-  'notifications.clear': 'Limpiar notificaciones',
-  'notifications.marked_clear_confirmation': '¿Deseas borrar permanentemente todas las notificaciones seleccionadas?',
-  'notifications.marked_clear': 'Limpiar notificaciones seleccionadas',
-  'onboarding.page_one.federation': '{domain} es una "instancia" de Mastodon. Mastodon es una red de servidores independientes que se unen para crear una red social más grande. A estos servidores los llamamos instancias.',
-  'onboarding.page_one.welcome': '¡Bienvenidx a {domain}!',
-  'onboarding.page_six.github': '{domain} usa Glitchsoc. Glitchsoc es una bifurcación {fork} amigable de {Mastodon}, y es compatible con cualquier instancia o aplicación de Mastodon. Glitchsoc es completamente gratuito y de código abierto. Puedes reportar errores, solicitar funciones o contribuir al código en {github}.',
-  'settings.always_show_spoilers_field': 'Siempre mostrar el campo de advertencia de contenido',
-  'settings.auto_collapse_all': 'Todo',
-  'settings.auto_collapse_lengthy': 'Toots largos',
-  'settings.auto_collapse_media': 'Toots con medios',
-  'settings.auto_collapse_notifications': 'Notificaciones',
-  'settings.auto_collapse_reblogs': 'Retoots',
-  'settings.auto_collapse_replies': 'Respuestas',
-  'settings.auto_collapse': 'Colapsar automáticamente',
-  'settings.close': 'Cerrar',
-  'settings.collapsed_statuses': 'Toots colapsados',
-  'settings.compose_box_opts': 'Cuadro de redacción',
-  'settings.confirm_before_clearing_draft': 'Mostrar diálogo de confirmación antes de sobreescribir un mensaje estabas escribiendo',
-  'settings.confirm_boost_missing_media_description': 'Mostrar diálogo de confirmación antes de retootear publicaciones con medios sin descripción',
-  'settings.confirm_missing_media_description': 'Mostrar diálogo de confirmación antes de publicar toots con medios sin descripción',
-  'settings.content_warnings_filter': 'No descolapsar estas advertencias de contenido:',
-  'settings.content_warnings.regexp': 'Regexp (expresión regular)',
-  'settings.content_warnings': 'Advertencias de contenido',
-  'settings.enable_collapsed': 'Habilitar toots colapsados',
-  'settings.enable_content_warnings_auto_unfold': 'Descolapsar automáticamente advertencias de contenido',
-  'settings.filtering_behavior.cw': 'Mostrar el toot y agregar las palabras filtradas a la advertencia de contenido',
-  'settings.filtering_behavior.drop': 'Ocultar toots filtrados por completo',
-  'settings.filtering_behavior.hide': 'Mostrar "Filtrado" y agregar un botón para saber por qué',
-  'settings.filtering_behavior.upstream': 'Mostrar "Filtrado"',
-  'settings.filtering_behavior': 'Configuración de filtros',
-  'settings.filters': 'Filtros',
-  'settings.general': 'General',
-  'settings.hicolor_privacy_icons': 'Íconos de privacidad más visibles',
-  'settings.image_backgrounds_media': 'Vista previa de medios de toots colapsados',
-  'settings.image_backgrounds_users': 'Darle fondo de imagen a toots colapsados',
-  'settings.image_backgrounds': 'Fondos de imágenes',
-  'settings.inline_preview_cards': 'Vista previa para enlaces externos',
-  'settings.layout_opts': 'Opciones de diseño',
-  'settings.layout': 'Diseño',
-  'settings.media_fullwidth': 'Ancho completo al mostrar medios ',
-  'settings.media_letterbox_hint': 'Escalar medios para que llenen el espacio del contenedor sin cambiar sus proporciones sin recortarlos',
-  'settings.media_letterbox': 'Mantener proporciones al mostrar medios',
-  'settings.media_reveal_behind_cw': 'Siempre mostrar medios sensibles dentro de las advertencias de contenido',
-  'settings.media': 'Medios',
-  'settings.navbar_under': 'Barra de navegación en la parte inferior (solo móvil)',
-  'settings.notifications_opts': 'Opciones de notificaciones',
-  'settings.notifications.favicon_badge.hint': 'Muestra un marcador de notificaciones sin leer en el favicon',
-  'settings.notifications.favicon_badge': 'Marcador de notificaciones en el favicon',
-  'settings.notifications.tab_badge.hint': 'Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta',
-  'settings.notifications.tab_badge': 'Marcador de notificaciones no leídas',
-  'settings.preferences': 'Preferencias de usuarix',
-  'settings.prepend_cw_re': 'Anteponer "re: " a las advertencias de contenido al responder',
-  'settings.preselect_on_reply_hint': 'Al responder a conversaciones con múltiples participantes, preselecciona los nombres de usuarix subsecuentes del/la primerx',
-  'settings.preselect_on_reply': 'Preseleccionar nombres de usuarix al responder',
-  'settings.rewrite_mentions_acct': 'Reescribir con nombre de usuarix y dominio (para cuentas remotas)',
-  'settings.rewrite_mentions_no': 'No reescribir menciones',
-  'settings.rewrite_mentions_username': 'Reescribir con nombre de usuarix',
-  'settings.rewrite_mentions': 'Reescribir menciones in publicaciones mostradas',
-  'settings.show_action_bar': 'Mostrar botones de acción en toots colapsados',
-  'settings.show_content_type_choice': 'Mostrar selección de tipo de contenido al crear toots',
-  'settings.show_reply_counter': 'Mostrar un conteo estimado de respuestas',
-  'settings.side_arm_reply_mode.copy': 'Copiar opción de privacidad del toot al que estás respondiendo',
-  'settings.side_arm_reply_mode.keep': 'Conservar opción de privacidad',
-  'settings.side_arm_reply_mode.restrict': 'Restringir la opción de privacidad a la misma del toot al que estás respondiendo',
-  'settings.side_arm_reply_mode': 'Al responder a un toot, el botón de toot secundario debe:',
-  'settings.side_arm.none': 'Ninguno',
-  'settings.side_arm': 'Botón secundario:',
-  'settings.swipe_to_change_columns': 'Permitir deslizar para cambiar columnas (Sólo en móvil)',
-  'settings.tag_misleading_links.hint': 'Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente',
-  'settings.tag_misleading_links': 'Marcar enlaces engañosos',
-  'settings.wide_view': 'Vista amplia (solo modo de escritorio)',
-  'status.collapse': 'Colapsar',
-  'status.uncollapse': 'Descolapsar',
-  'confirmations.unfilter': 'Información del toot filtrado',
-  'confirmations.unfilter.author': 'Publicado por',
-  'confirmations.unfilter.filters': 'Coincidencia con {count, plural, one {filtro} other {filtros}}',
-  'confirmations.unfilter.edit_filter': 'Editar filtro',
-  'confirmations.unfilter.confirm': 'Mostrar',
-  'confirmations.delete.confirm': 'Borrar',
-  'confirmations.delete.message': 'Por favor confirma borrado',
-  'confirmations.redraft.confirm': 'Borrar y volver a borrador',
-  'confirmations.redraft.message': '¿Borrar y volver a borrador? Perderás todas las respuestas, retoots y favoritos asociados, y las respuestas a la publicación original quedarán huérfanos.',
-  'confirmations.reply.confirm': 'Responder',
-  'confirmations.reply.message': 'Responder no sobreescribirá el mensaje que estás escibiendo actualmente. ¿Deseas continuar?',
-  'status.show_filter_reason': '(mostrar por qué)',
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/es.json b/app/javascript/flavours/glitch/locales/es.json
new file mode 100644
index 000000000..07acbf914
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/es.json
@@ -0,0 +1,95 @@
+{
+  "advanced_options.icon_title": "Opciones avanzadas",
+  "advanced_options.local-only.long": "No publicar a otras instancias",
+  "advanced_options.local-only.short": "Local",
+  "advanced_options.local-only.tooltip": "Este toot es local",
+  "advanced_options.threaded_mode.long": "Al publicar abre automáticamente una respuesta",
+  "advanced_options.threaded_mode.short": "Modo hilo",
+  "advanced_options.threaded_mode.tooltip": "Modo hilo habilitado",
+  "compose.attach": "Adjuntar...",
+  "compose.attach.doodle": "Dibujar algo",
+  "compose.attach.upload": "Subir un archivo",
+  "confirmations.unfilter.author": "Publicado por",
+  "confirmations.unfilter.confirm": "Mostrar",
+  "confirmations.unfilter.edit_filter": "Editar filtro",
+  "confirmations.unfilter.filters": "Coincidencia con {count, plural, one {filtro} other {filtros}}",
+  "favourite_modal.combo": "Puedes presionar {combo} para omitir esto la próxima vez",
+  "getting_started.onboarding": "Paseo inicial",
+  "home.column_settings.show_direct": "Mostrar mensajes directos",
+  "layout.auto": "Automático",
+  "layout.desktop": "Escritorio",
+  "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y tamaño de pantalla",
+  "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "media_gallery.sensitive": "Sensible",
+  "navigation_bar.app_settings": "Ajustes de aplicación",
+  "notification.markForDeletion": "Marcar para borrar",
+  "notification_purge.btn_all": "Seleccionar\ntodo",
+  "notification_purge.btn_apply": "Borrar\nselección",
+  "notification_purge.btn_invert": "Invertir\nselección",
+  "notification_purge.btn_none": "Seleccionar\nnada",
+  "notifications.marked_clear": "Limpiar notificaciones seleccionadas",
+  "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?",
+  "onboarding.page_one.federation": "{domain} es una \"instancia\" de Mastodon. Mastodon es una red de servidores independientes que se unen para crear una red social más grande. A estos servidores los llamamos instancias.",
+  "onboarding.page_one.welcome": "¡Bienvenidx a {domain}!",
+  "onboarding.page_six.github": "{domain} usa Glitchsoc. Glitchsoc es una bifurcación {fork} amigable de {Mastodon}, y es compatible con cualquier instancia o aplicación de Mastodon. Glitchsoc es completamente gratuito y de código abierto. Puedes reportar errores, solicitar funciones o contribuir al código en {github}.",
+  "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
+  "settings.auto_collapse": "Colapsar automáticamente",
+  "settings.auto_collapse_all": "Todo",
+  "settings.auto_collapse_lengthy": "Toots largos",
+  "settings.auto_collapse_media": "Toots con medios",
+  "settings.auto_collapse_notifications": "Notificaciones",
+  "settings.auto_collapse_reblogs": "Retoots",
+  "settings.auto_collapse_replies": "Respuestas",
+  "settings.close": "Cerrar",
+  "settings.collapsed_statuses": "Toots colapsados",
+  "settings.compose_box_opts": "Cuadro de redacción",
+  "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmación antes de sobreescribir un mensaje estabas escribiendo",
+  "settings.confirm_boost_missing_media_description": "Mostrar diálogo de confirmación antes de retootear publicaciones con medios sin descripción",
+  "settings.confirm_missing_media_description": "Mostrar diálogo de confirmación antes de publicar toots con medios sin descripción",
+  "settings.content_warnings": "Advertencias de contenido",
+  "settings.content_warnings.regexp": "Regexp (expresión regular)",
+  "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:",
+  "settings.enable_collapsed": "Habilitar toots colapsados",
+  "settings.enable_content_warnings_auto_unfold": "Descolapsar automáticamente advertencias de contenido",
+  "settings.hicolor_privacy_icons": "Íconos de privacidad más visibles",
+  "settings.image_backgrounds": "Fondos de imágenes",
+  "settings.image_backgrounds_media": "Vista previa de medios de toots colapsados",
+  "settings.image_backgrounds_users": "Darle fondo de imagen a toots colapsados",
+  "settings.inline_preview_cards": "Vista previa para enlaces externos",
+  "settings.layout": "Diseño",
+  "settings.layout_opts": "Opciones de diseño",
+  "settings.media": "Medios",
+  "settings.media_fullwidth": "Ancho completo al mostrar medios ",
+  "settings.media_letterbox": "Mantener proporciones al mostrar medios",
+  "settings.media_letterbox_hint": "Escalar medios para que llenen el espacio del contenedor sin cambiar sus proporciones sin recortarlos",
+  "settings.media_reveal_behind_cw": "Siempre mostrar medios sensibles dentro de las advertencias de contenido",
+  "settings.notifications.favicon_badge": "Marcador de notificaciones en el favicon",
+  "settings.notifications.favicon_badge.hint": "Muestra un marcador de notificaciones sin leer en el favicon",
+  "settings.notifications.tab_badge": "Marcador de notificaciones no leídas",
+  "settings.notifications.tab_badge.hint": "Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta",
+  "settings.notifications_opts": "Opciones de notificaciones",
+  "settings.preferences": "Preferencias de usuarix",
+  "settings.prepend_cw_re": "Anteponer \"re: \" a las advertencias de contenido al responder",
+  "settings.preselect_on_reply": "Preseleccionar nombres de usuarix al responder",
+  "settings.preselect_on_reply_hint": "Al responder a conversaciones con múltiples participantes, preselecciona los nombres de usuarix subsecuentes del/la primerx",
+  "settings.rewrite_mentions": "Reescribir menciones in publicaciones mostradas",
+  "settings.rewrite_mentions_acct": "Reescribir con nombre de usuarix y dominio (para cuentas remotas)",
+  "settings.rewrite_mentions_no": "No reescribir menciones",
+  "settings.rewrite_mentions_username": "Reescribir con nombre de usuarix",
+  "settings.show_action_bar": "Mostrar botones de acción en toots colapsados",
+  "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear toots",
+  "settings.show_reply_counter": "Mostrar un conteo estimado de respuestas",
+  "settings.side_arm": "Botón secundario:",
+  "settings.side_arm.none": "Ninguno",
+  "settings.side_arm_reply_mode": "Al responder a un toot, el botón de toot secundario debe:",
+  "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad del toot al que estás respondiendo",
+  "settings.side_arm_reply_mode.keep": "Conservar opción de privacidad",
+  "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma del toot al que estás respondiendo",
+  "settings.swipe_to_change_columns": "Permitir deslizar para cambiar columnas (Sólo en móvil)",
+  "settings.tag_misleading_links": "Marcar enlaces engañosos",
+  "settings.tag_misleading_links.hint": "Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente",
+  "settings.wide_view": "Vista amplia (solo modo de escritorio)",
+  "status.collapse": "Colapsar",
+  "status.uncollapse": "Descolapsar"
+}
diff --git a/app/javascript/flavours/glitch/locales/et.js b/app/javascript/flavours/glitch/locales/et.js
deleted file mode 100644
index e3ea6b2a9..000000000
--- a/app/javascript/flavours/glitch/locales/et.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/et.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/et.json b/app/javascript/flavours/glitch/locales/et.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/et.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/eu.js b/app/javascript/flavours/glitch/locales/eu.js
deleted file mode 100644
index 946410b67..000000000
--- a/app/javascript/flavours/glitch/locales/eu.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/eu.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/eu.json b/app/javascript/flavours/glitch/locales/eu.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/eu.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/fa.js b/app/javascript/flavours/glitch/locales/fa.js
deleted file mode 100644
index d82461a1a..000000000
--- a/app/javascript/flavours/glitch/locales/fa.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/fa.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/fa.json b/app/javascript/flavours/glitch/locales/fa.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/fa.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/fi.js b/app/javascript/flavours/glitch/locales/fi.js
deleted file mode 100644
index 11c3cd082..000000000
--- a/app/javascript/flavours/glitch/locales/fi.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/fi.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/fi.json b/app/javascript/flavours/glitch/locales/fi.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/fi.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/fo.json b/app/javascript/flavours/glitch/locales/fo.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/fo.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/fr-QC.json b/app/javascript/flavours/glitch/locales/fr-QC.json
new file mode 100644
index 000000000..383d933b4
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/fr-QC.json
@@ -0,0 +1,200 @@
+{
+  "about.fork_disclaimer": "Glitch-soc est un logiciel gratuit et open source, fork de Mastodon.",
+  "account.add_account_note": "Ajouter une note pour @{name}",
+  "account.disclaimer_full": "Les informations ci-dessous peuvent être incomplètes.",
+  "account.follows": "Abonnements",
+  "account.joined": "Ici depuis {date}",
+  "account.suspended_disclaimer_full": "Cet utilisateur a été suspendu par un modérateur.",
+  "account.view_full_profile": "Voir le profil complet",
+  "account_note.cancel": "Annuler",
+  "account_note.edit": "Éditer",
+  "account_note.glitch_placeholder": "Aucun commentaire fourni",
+  "account_note.save": "Sauvegarder",
+  "advanced_options.icon_title": "Options avancées",
+  "advanced_options.local-only.long": "Ne pas envoyer aux autres instances",
+  "advanced_options.local-only.short": "Uniquement en local",
+  "advanced_options.local-only.tooltip": "Ce post est uniquement local",
+  "advanced_options.threaded_mode.long": "Ouvre automatiquement une réponse lors de la publication",
+  "advanced_options.threaded_mode.short": "Mode thread",
+  "advanced_options.threaded_mode.tooltip": "Mode thread activé",
+  "boost_modal.missing_description": "Ce post contient des médias sans description",
+  "column.favourited_by": "Ajouté en favori par",
+  "column.heading": "Divers",
+  "column.reblogged_by": "Partagé par",
+  "column.subheading": "Autres options",
+  "column_header.profile": "Profil",
+  "column_subheading.lists": "Listes",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Afficher seulement les posts locaux",
+  "compose.attach": "Joindre…",
+  "compose.attach.doodle": "Dessiner quelque chose",
+  "compose.attach.upload": "Téléverser un fichier",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Text brut",
+  "compose_form.poll.multiple_choices": "Choix multiples",
+  "compose_form.poll.single_choice": "Choix unique",
+  "compose_form.spoiler": "Cacher le texte derrière un avertissement",
+  "confirmation_modal.do_not_ask_again": "Ne plus demander confirmation",
+  "confirmations.deprecated_settings.confirm": "Utiliser les préférences de Mastodon",
+  "confirmations.deprecated_settings.message": "Certaines {app_settings} de glitch-soc que vous utilisez ont été remplacées par les {preferences} de Mastodon et seront remplacées :",
+  "confirmations.missing_media_description.confirm": "Envoyer quand même",
+  "confirmations.missing_media_description.edit": "Modifier le média",
+  "confirmations.missing_media_description.message": "Au moins un média joint manque d'une description. Pensez à décrire tous les médias attachés pour les malvoyant·e·s avant de publier votre post.",
+  "confirmations.unfilter.author": "Auteur",
+  "confirmations.unfilter.confirm": "Afficher",
+  "confirmations.unfilter.edit_filter": "Modifier le filtre",
+  "confirmations.unfilter.filters": "Correspondance avec {count, plural, one {un filtre} other {plusieurs filtres}}",
+  "content-type.change": "Type de contenu",
+  "direct.group_by_conversations": "Grouper par conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Comptes mis en avant",
+  "favourite_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci la prochaine fois",
+  "getting_started.onboarding": "Montre-moi les alentours",
+  "home.column_settings.advanced": "Avancé",
+  "home.column_settings.filter_regex": "Filtrer par expression régulière",
+  "home.column_settings.show_direct": "Afficher les MPs",
+  "home.settings": "Paramètres de la colonne",
+  "keyboard_shortcuts.bookmark": "ajouter aux marque-pages",
+  "keyboard_shortcuts.secondary_toot": "Envoyer le post en utilisant les paramètres secondaires de confidentialité",
+  "keyboard_shortcuts.toggle_collapse": "Plier/déplier les posts",
+  "layout.auto": "Auto",
+  "layout.desktop": "Ordinateur",
+  "layout.hint.auto": "Choisir automatiquement la mise en page selon l'option \"Activer l'interface Web avancée\" et la taille d'écran.",
+  "layout.hint.desktop": "Utiliser la mise en page en plusieurs colonnes indépendamment de l'option \"Activer l'interface Web avancée\" ou de la taille d'écran.",
+  "layout.hint.single": "Utiliser la mise en page à colonne unique indépendamment de l'option \"Activer l'interface Web avancée\" ou de la taille d'écran.",
+  "layout.single": "Téléphone",
+  "media_gallery.sensitive": "Sensible",
+  "moved_to_warning": "Ce compte a déménagé vers {moved_to_link} et ne peut donc plus accepter de nouveaux abonné·e·s.",
+  "navigation_bar.app_settings": "Paramètres de l'application",
+  "navigation_bar.featured_users": "Utilisateurs mis en avant",
+  "navigation_bar.info": "Informations détaillées",
+  "navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
+  "navigation_bar.misc": "Autres",
+  "notification.markForDeletion": "Ajouter aux éléments à supprimer",
+  "notification_purge.btn_all": "Sélectionner\ntout",
+  "notification_purge.btn_apply": "Effacer\nla sélection",
+  "notification_purge.btn_invert": "Inverser\nla sélection",
+  "notification_purge.btn_none": "Annuler\nla sélection",
+  "notification_purge.start": "Activer le mode de nettoyage des notifications",
+  "notifications.marked_clear": "Effacer les notifications sélectionnées",
+  "notifications.marked_clear_confirmation": "Voulez-vous vraiment effacer de manière permanente toutes les notifications sélectionnées ?",
+  "onboarding.done": "Terminé",
+  "onboarding.next": "Suivant",
+  "onboarding.page_five.public_timelines": "Le fil local affiche les posts publics de tout le monde sur {domain}. Le fil global affiche les posts publics de tous les comptes que les personnes de {domain} suivent. Ce sont les fils publics, une façon formidable de découvrir de nouvelles personnes.",
+  "onboarding.page_four.home": "L'accueil affiche les posts des personnes que vous suivez.",
+  "onboarding.page_four.notifications": "La colonne de notifications vous montre lorsque quelqu'un interagit avec vous.",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "Vous êtes sur {domain}, donc votre nom d'utilisateur complet est {handle}",
+  "onboarding.page_one.welcome": "Bievenue sur {domain} !",
+  "onboarding.page_six.admin": "Votre admin d’instance est {admin}.",
+  "onboarding.page_six.almost_done": "C'est bientôt fini...",
+  "onboarding.page_six.appetoot": "Bon appétoot !",
+  "onboarding.page_six.apps_available": "Il y a des {apps} disponibles pour iOS, Android et d'autres plateformes.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "règles de la communauté",
+  "onboarding.page_six.read_guidelines": "Veuillez lire les {guidelines} de {domain} !",
+  "onboarding.page_six.various_app": "applications mobiles",
+  "onboarding.page_three.profile": "Modifiez votre profil pour changer votre avatar, biographie et nom public. Ici, vous trouverez également d'autres options.",
+  "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des personnes et regarder les hashtags comme {illustration} et {introductions}. Pour chercher une personne n'étant pas sur cette instance, utilisez son nom d'utilisateur complet.",
+  "onboarding.page_two.compose": "Écrivez des posts depuis la colonne de rédaction. Vous pouvez téléverser des images, changer la confidentialité et ajouter des avertissements de contenu avec les boutons ci-dessous.",
+  "onboarding.skip": "Passer",
+  "settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu",
+  "settings.auto_collapse": "Repliage automatique",
+  "settings.auto_collapse_all": "Tout",
+  "settings.auto_collapse_lengthy": "Posts longs",
+  "settings.auto_collapse_media": "Posts avec média",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Réponses",
+  "settings.close": "Fermer",
+  "settings.collapsed_statuses": "Posts repliés",
+  "settings.compose_box_opts": "Zone de rédaction",
+  "settings.confirm_before_clearing_draft": "Afficher une fenêtre de confirmation avant d'écraser le message en cours de rédaction",
+  "settings.confirm_boost_missing_media_description": "Afficher une fenêtre de confirmation avant de partager des posts manquant de description des médias",
+  "settings.confirm_missing_media_description": "Afficher une fenêtre de confirmation avant de publier des posts manquant de description de média",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Expression rationnelle",
+  "settings.content_warnings_filter": "Avertissement de contenu à ne pas automatiquement déplier :",
+  "settings.content_warnings_media_outside": "Afficher les médias en dehors des avertissements de contenu",
+  "settings.content_warnings_media_outside_hint": "Reproduit le comportement par défaut de Mastodon, les médias attachés ne sont plus affectés par le bouton d'affichage d'un post avec avertissement",
+  "settings.content_warnings_shared_state": "Affiche/cache le contenu de toutes les copies à la fois",
+  "settings.content_warnings_shared_state_hint": "Reproduit le comportement par défaut de Mastodon, le bouton d'avertissement de contenu affecte toutes les copies d'un post à la fois. Cela empêchera le repliement automatique de n'importe quelle copie d'un post avec un avertissement déplié",
+  "settings.content_warnings_unfold_opts": "Options de dépliement automatique",
+  "settings.deprecated_setting": "Cette option est maintenant définie par les {settings_page_link} de Mastodon",
+  "settings.enable_collapsed": "Activer le repliement des posts",
+  "settings.enable_collapsed_hint": "Les posts repliés ont une partie de leur contenu caché pour libérer de l'espace sur l'écran. C'est une option différente de l'avertissement de contenu",
+  "settings.enable_content_warnings_auto_unfold": "Déplier automatiquement les avertissements de contenu",
+  "settings.general": "Général",
+  "settings.hicolor_privacy_icons": "Indicateurs de confidentialité en couleurs",
+  "settings.hicolor_privacy_icons.hint": "Affiche les indicateurs de confidentialité dans des couleurs facilement distinguables",
+  "settings.image_backgrounds": "Images en arrière-plan",
+  "settings.image_backgrounds_media": "Prévisualiser les médias d'un post replié",
+  "settings.image_backgrounds_media_hint": "Si le post a un média attaché, utiliser le premier comme arrière-plan du post",
+  "settings.image_backgrounds_users": "Donner aux posts repliés une image en arrière-plan",
+  "settings.inline_preview_cards": "Cartes d'aperçu pour les liens externes",
+  "settings.layout": "Mise en page :",
+  "settings.layout_opts": "Mise en page",
+  "settings.media": "Média",
+  "settings.media_fullwidth": "Utiliser toute la largeur pour les aperçus",
+  "settings.media_letterbox": "Afficher les médias en Letterbox",
+  "settings.media_letterbox_hint": "Réduit le média et utilise une letterbox pour afficher l'image entière plutôt que de l'étirer et de la rogner",
+  "settings.media_reveal_behind_cw": "Toujours afficher les médias sensibles avec avertissement",
+  "settings.notifications.favicon_badge": "Badge de notifications non lues dans la favicon",
+  "settings.notifications.favicon_badge.hint": "Ajoute un badge dans la favicon pour alerter d'une notification non lue",
+  "settings.notifications.tab_badge": "Badge de notifications non lues",
+  "settings.notifications.tab_badge.hint": "Affiche un badge de notifications non lues dans les icônes des colonnes quand la colonne n'est pas ouverte",
+  "settings.notifications_opts": "Options des notifications",
+  "settings.pop_in_left": "Gauche",
+  "settings.pop_in_player": "Activer le lecteur pop-in",
+  "settings.pop_in_position": "Position du lecteur pop-in :",
+  "settings.pop_in_right": "Droite",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Préfixer les avertissements avec \"re: \" lors d'une réponse",
+  "settings.preselect_on_reply": "Présélectionner les noms d’utilisateur·rices lors de la réponse",
+  "settings.preselect_on_reply_hint": "Présélectionner les noms d'utilisateurs après le premier lors d'une réponse à une conversation à plusieurs participants",
+  "settings.rewrite_mentions": "Réécrire les mentions dans les posts affichés",
+  "settings.rewrite_mentions_acct": "Réécrire avec le nom d'utilisateur·rice et le domaine (lorsque le compte est distant)",
+  "settings.rewrite_mentions_no": "Ne pas réécrire les mentions",
+  "settings.rewrite_mentions_username": "Réécrire avec le nom d’utilisateur·rice",
+  "settings.shared_settings_link": "préférences de l'utilisateur",
+  "settings.show_action_bar": "Afficher les boutons d'action dans les posts repliés",
+  "settings.show_content_type_choice": "Afficher le choix du type de contenu lors de la création des posts",
+  "settings.show_reply_counter": "Afficher une estimation du nombre de réponses",
+  "settings.side_arm": "Bouton secondaire de publication :",
+  "settings.side_arm.none": "Aucun",
+  "settings.side_arm_reply_mode": "Quand vous répondez à un post, le bouton secondaire de publication devrait :",
+  "settings.side_arm_reply_mode.copy": "Copier la confidentialité du post auquel vous répondez",
+  "settings.side_arm_reply_mode.keep": "Garder la confidentialité établie",
+  "settings.side_arm_reply_mode.restrict": "Restreindre la confidentialité de la réponse à celle du post auquel vous répondez",
+  "settings.status_icons": "Icônes des posts",
+  "settings.status_icons_language": "Indicateur de langue",
+  "settings.status_icons_local_only": "Indicateur de post local",
+  "settings.status_icons_media": "Indicateur de médias et sondage",
+  "settings.status_icons_reply": "Indicateur de réponses",
+  "settings.status_icons_visibility": "Indicateur de la confidentialité du post",
+  "settings.swipe_to_change_columns": "Glissement latéral pour changer de colonne (mobile uniquement)",
+  "settings.tag_misleading_links": "Étiqueter les liens trompeurs",
+  "settings.tag_misleading_links.hint": "Ajouter une indication visuelle avec l'hôte cible du lien à chaque lien ne le mentionnant pas explicitement",
+  "settings.wide_view": "Vue élargie (mode ordinateur uniquement)",
+  "settings.wide_view_hint": "Étire les colonnes pour mieux remplir l'espace disponible.",
+  "status.collapse": "Replier",
+  "status.has_audio": "Contient des fichiers audio attachés",
+  "status.has_pictures": "Contient des images attachées",
+  "status.has_preview_card": "Contient une carte de prévisualisation attachée",
+  "status.has_video": "Contient des vidéos attachées",
+  "status.in_reply_to": "Ce post est une réponse",
+  "status.is_poll": "Ce post est un sondage",
+  "status.local_only": "Visible uniquement depuis votre instance",
+  "status.sensitive_toggle": "Cliquer pour voir",
+  "status.uncollapse": "Déplier",
+  "web_app_crash.change_your_settings": "Changez vos {settings}",
+  "web_app_crash.content": "Voici les différentes options qui s'offrent à vous :",
+  "web_app_crash.debug_info": "Informations de débogage",
+  "web_app_crash.disable_addons": "Désactivez les extensions de votre navigateur, ainsi que les outils de traduction intégrés",
+  "web_app_crash.issue_tracker": "traqueur d'erreurs",
+  "web_app_crash.reload": "Rafraichir",
+  "web_app_crash.reload_page": "{reload} la page actuelle",
+  "web_app_crash.report_issue": "Signalez un bug dans le {issuetracker}",
+  "web_app_crash.settings": "paramètres",
+  "web_app_crash.title": "Nous sommes navrés, mais quelque chose s'est mal passé dans l'application Mastodon."
+}
diff --git a/app/javascript/flavours/glitch/locales/fr.js b/app/javascript/flavours/glitch/locales/fr.js
deleted file mode 100644
index 8562f5594..000000000
--- a/app/javascript/flavours/glitch/locales/fr.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/fr.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/fr.json b/app/javascript/flavours/glitch/locales/fr.json
new file mode 100644
index 000000000..383d933b4
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/fr.json
@@ -0,0 +1,200 @@
+{
+  "about.fork_disclaimer": "Glitch-soc est un logiciel gratuit et open source, fork de Mastodon.",
+  "account.add_account_note": "Ajouter une note pour @{name}",
+  "account.disclaimer_full": "Les informations ci-dessous peuvent être incomplètes.",
+  "account.follows": "Abonnements",
+  "account.joined": "Ici depuis {date}",
+  "account.suspended_disclaimer_full": "Cet utilisateur a été suspendu par un modérateur.",
+  "account.view_full_profile": "Voir le profil complet",
+  "account_note.cancel": "Annuler",
+  "account_note.edit": "Éditer",
+  "account_note.glitch_placeholder": "Aucun commentaire fourni",
+  "account_note.save": "Sauvegarder",
+  "advanced_options.icon_title": "Options avancées",
+  "advanced_options.local-only.long": "Ne pas envoyer aux autres instances",
+  "advanced_options.local-only.short": "Uniquement en local",
+  "advanced_options.local-only.tooltip": "Ce post est uniquement local",
+  "advanced_options.threaded_mode.long": "Ouvre automatiquement une réponse lors de la publication",
+  "advanced_options.threaded_mode.short": "Mode thread",
+  "advanced_options.threaded_mode.tooltip": "Mode thread activé",
+  "boost_modal.missing_description": "Ce post contient des médias sans description",
+  "column.favourited_by": "Ajouté en favori par",
+  "column.heading": "Divers",
+  "column.reblogged_by": "Partagé par",
+  "column.subheading": "Autres options",
+  "column_header.profile": "Profil",
+  "column_subheading.lists": "Listes",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Afficher seulement les posts locaux",
+  "compose.attach": "Joindre…",
+  "compose.attach.doodle": "Dessiner quelque chose",
+  "compose.attach.upload": "Téléverser un fichier",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Text brut",
+  "compose_form.poll.multiple_choices": "Choix multiples",
+  "compose_form.poll.single_choice": "Choix unique",
+  "compose_form.spoiler": "Cacher le texte derrière un avertissement",
+  "confirmation_modal.do_not_ask_again": "Ne plus demander confirmation",
+  "confirmations.deprecated_settings.confirm": "Utiliser les préférences de Mastodon",
+  "confirmations.deprecated_settings.message": "Certaines {app_settings} de glitch-soc que vous utilisez ont été remplacées par les {preferences} de Mastodon et seront remplacées :",
+  "confirmations.missing_media_description.confirm": "Envoyer quand même",
+  "confirmations.missing_media_description.edit": "Modifier le média",
+  "confirmations.missing_media_description.message": "Au moins un média joint manque d'une description. Pensez à décrire tous les médias attachés pour les malvoyant·e·s avant de publier votre post.",
+  "confirmations.unfilter.author": "Auteur",
+  "confirmations.unfilter.confirm": "Afficher",
+  "confirmations.unfilter.edit_filter": "Modifier le filtre",
+  "confirmations.unfilter.filters": "Correspondance avec {count, plural, one {un filtre} other {plusieurs filtres}}",
+  "content-type.change": "Type de contenu",
+  "direct.group_by_conversations": "Grouper par conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Comptes mis en avant",
+  "favourite_modal.combo": "Vous pouvez appuyer sur {combo} pour passer ceci la prochaine fois",
+  "getting_started.onboarding": "Montre-moi les alentours",
+  "home.column_settings.advanced": "Avancé",
+  "home.column_settings.filter_regex": "Filtrer par expression régulière",
+  "home.column_settings.show_direct": "Afficher les MPs",
+  "home.settings": "Paramètres de la colonne",
+  "keyboard_shortcuts.bookmark": "ajouter aux marque-pages",
+  "keyboard_shortcuts.secondary_toot": "Envoyer le post en utilisant les paramètres secondaires de confidentialité",
+  "keyboard_shortcuts.toggle_collapse": "Plier/déplier les posts",
+  "layout.auto": "Auto",
+  "layout.desktop": "Ordinateur",
+  "layout.hint.auto": "Choisir automatiquement la mise en page selon l'option \"Activer l'interface Web avancée\" et la taille d'écran.",
+  "layout.hint.desktop": "Utiliser la mise en page en plusieurs colonnes indépendamment de l'option \"Activer l'interface Web avancée\" ou de la taille d'écran.",
+  "layout.hint.single": "Utiliser la mise en page à colonne unique indépendamment de l'option \"Activer l'interface Web avancée\" ou de la taille d'écran.",
+  "layout.single": "Téléphone",
+  "media_gallery.sensitive": "Sensible",
+  "moved_to_warning": "Ce compte a déménagé vers {moved_to_link} et ne peut donc plus accepter de nouveaux abonné·e·s.",
+  "navigation_bar.app_settings": "Paramètres de l'application",
+  "navigation_bar.featured_users": "Utilisateurs mis en avant",
+  "navigation_bar.info": "Informations détaillées",
+  "navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
+  "navigation_bar.misc": "Autres",
+  "notification.markForDeletion": "Ajouter aux éléments à supprimer",
+  "notification_purge.btn_all": "Sélectionner\ntout",
+  "notification_purge.btn_apply": "Effacer\nla sélection",
+  "notification_purge.btn_invert": "Inverser\nla sélection",
+  "notification_purge.btn_none": "Annuler\nla sélection",
+  "notification_purge.start": "Activer le mode de nettoyage des notifications",
+  "notifications.marked_clear": "Effacer les notifications sélectionnées",
+  "notifications.marked_clear_confirmation": "Voulez-vous vraiment effacer de manière permanente toutes les notifications sélectionnées ?",
+  "onboarding.done": "Terminé",
+  "onboarding.next": "Suivant",
+  "onboarding.page_five.public_timelines": "Le fil local affiche les posts publics de tout le monde sur {domain}. Le fil global affiche les posts publics de tous les comptes que les personnes de {domain} suivent. Ce sont les fils publics, une façon formidable de découvrir de nouvelles personnes.",
+  "onboarding.page_four.home": "L'accueil affiche les posts des personnes que vous suivez.",
+  "onboarding.page_four.notifications": "La colonne de notifications vous montre lorsque quelqu'un interagit avec vous.",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "Vous êtes sur {domain}, donc votre nom d'utilisateur complet est {handle}",
+  "onboarding.page_one.welcome": "Bievenue sur {domain} !",
+  "onboarding.page_six.admin": "Votre admin d’instance est {admin}.",
+  "onboarding.page_six.almost_done": "C'est bientôt fini...",
+  "onboarding.page_six.appetoot": "Bon appétoot !",
+  "onboarding.page_six.apps_available": "Il y a des {apps} disponibles pour iOS, Android et d'autres plateformes.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "règles de la communauté",
+  "onboarding.page_six.read_guidelines": "Veuillez lire les {guidelines} de {domain} !",
+  "onboarding.page_six.various_app": "applications mobiles",
+  "onboarding.page_three.profile": "Modifiez votre profil pour changer votre avatar, biographie et nom public. Ici, vous trouverez également d'autres options.",
+  "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des personnes et regarder les hashtags comme {illustration} et {introductions}. Pour chercher une personne n'étant pas sur cette instance, utilisez son nom d'utilisateur complet.",
+  "onboarding.page_two.compose": "Écrivez des posts depuis la colonne de rédaction. Vous pouvez téléverser des images, changer la confidentialité et ajouter des avertissements de contenu avec les boutons ci-dessous.",
+  "onboarding.skip": "Passer",
+  "settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu",
+  "settings.auto_collapse": "Repliage automatique",
+  "settings.auto_collapse_all": "Tout",
+  "settings.auto_collapse_lengthy": "Posts longs",
+  "settings.auto_collapse_media": "Posts avec média",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Réponses",
+  "settings.close": "Fermer",
+  "settings.collapsed_statuses": "Posts repliés",
+  "settings.compose_box_opts": "Zone de rédaction",
+  "settings.confirm_before_clearing_draft": "Afficher une fenêtre de confirmation avant d'écraser le message en cours de rédaction",
+  "settings.confirm_boost_missing_media_description": "Afficher une fenêtre de confirmation avant de partager des posts manquant de description des médias",
+  "settings.confirm_missing_media_description": "Afficher une fenêtre de confirmation avant de publier des posts manquant de description de média",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Expression rationnelle",
+  "settings.content_warnings_filter": "Avertissement de contenu à ne pas automatiquement déplier :",
+  "settings.content_warnings_media_outside": "Afficher les médias en dehors des avertissements de contenu",
+  "settings.content_warnings_media_outside_hint": "Reproduit le comportement par défaut de Mastodon, les médias attachés ne sont plus affectés par le bouton d'affichage d'un post avec avertissement",
+  "settings.content_warnings_shared_state": "Affiche/cache le contenu de toutes les copies à la fois",
+  "settings.content_warnings_shared_state_hint": "Reproduit le comportement par défaut de Mastodon, le bouton d'avertissement de contenu affecte toutes les copies d'un post à la fois. Cela empêchera le repliement automatique de n'importe quelle copie d'un post avec un avertissement déplié",
+  "settings.content_warnings_unfold_opts": "Options de dépliement automatique",
+  "settings.deprecated_setting": "Cette option est maintenant définie par les {settings_page_link} de Mastodon",
+  "settings.enable_collapsed": "Activer le repliement des posts",
+  "settings.enable_collapsed_hint": "Les posts repliés ont une partie de leur contenu caché pour libérer de l'espace sur l'écran. C'est une option différente de l'avertissement de contenu",
+  "settings.enable_content_warnings_auto_unfold": "Déplier automatiquement les avertissements de contenu",
+  "settings.general": "Général",
+  "settings.hicolor_privacy_icons": "Indicateurs de confidentialité en couleurs",
+  "settings.hicolor_privacy_icons.hint": "Affiche les indicateurs de confidentialité dans des couleurs facilement distinguables",
+  "settings.image_backgrounds": "Images en arrière-plan",
+  "settings.image_backgrounds_media": "Prévisualiser les médias d'un post replié",
+  "settings.image_backgrounds_media_hint": "Si le post a un média attaché, utiliser le premier comme arrière-plan du post",
+  "settings.image_backgrounds_users": "Donner aux posts repliés une image en arrière-plan",
+  "settings.inline_preview_cards": "Cartes d'aperçu pour les liens externes",
+  "settings.layout": "Mise en page :",
+  "settings.layout_opts": "Mise en page",
+  "settings.media": "Média",
+  "settings.media_fullwidth": "Utiliser toute la largeur pour les aperçus",
+  "settings.media_letterbox": "Afficher les médias en Letterbox",
+  "settings.media_letterbox_hint": "Réduit le média et utilise une letterbox pour afficher l'image entière plutôt que de l'étirer et de la rogner",
+  "settings.media_reveal_behind_cw": "Toujours afficher les médias sensibles avec avertissement",
+  "settings.notifications.favicon_badge": "Badge de notifications non lues dans la favicon",
+  "settings.notifications.favicon_badge.hint": "Ajoute un badge dans la favicon pour alerter d'une notification non lue",
+  "settings.notifications.tab_badge": "Badge de notifications non lues",
+  "settings.notifications.tab_badge.hint": "Affiche un badge de notifications non lues dans les icônes des colonnes quand la colonne n'est pas ouverte",
+  "settings.notifications_opts": "Options des notifications",
+  "settings.pop_in_left": "Gauche",
+  "settings.pop_in_player": "Activer le lecteur pop-in",
+  "settings.pop_in_position": "Position du lecteur pop-in :",
+  "settings.pop_in_right": "Droite",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Préfixer les avertissements avec \"re: \" lors d'une réponse",
+  "settings.preselect_on_reply": "Présélectionner les noms d’utilisateur·rices lors de la réponse",
+  "settings.preselect_on_reply_hint": "Présélectionner les noms d'utilisateurs après le premier lors d'une réponse à une conversation à plusieurs participants",
+  "settings.rewrite_mentions": "Réécrire les mentions dans les posts affichés",
+  "settings.rewrite_mentions_acct": "Réécrire avec le nom d'utilisateur·rice et le domaine (lorsque le compte est distant)",
+  "settings.rewrite_mentions_no": "Ne pas réécrire les mentions",
+  "settings.rewrite_mentions_username": "Réécrire avec le nom d’utilisateur·rice",
+  "settings.shared_settings_link": "préférences de l'utilisateur",
+  "settings.show_action_bar": "Afficher les boutons d'action dans les posts repliés",
+  "settings.show_content_type_choice": "Afficher le choix du type de contenu lors de la création des posts",
+  "settings.show_reply_counter": "Afficher une estimation du nombre de réponses",
+  "settings.side_arm": "Bouton secondaire de publication :",
+  "settings.side_arm.none": "Aucun",
+  "settings.side_arm_reply_mode": "Quand vous répondez à un post, le bouton secondaire de publication devrait :",
+  "settings.side_arm_reply_mode.copy": "Copier la confidentialité du post auquel vous répondez",
+  "settings.side_arm_reply_mode.keep": "Garder la confidentialité établie",
+  "settings.side_arm_reply_mode.restrict": "Restreindre la confidentialité de la réponse à celle du post auquel vous répondez",
+  "settings.status_icons": "Icônes des posts",
+  "settings.status_icons_language": "Indicateur de langue",
+  "settings.status_icons_local_only": "Indicateur de post local",
+  "settings.status_icons_media": "Indicateur de médias et sondage",
+  "settings.status_icons_reply": "Indicateur de réponses",
+  "settings.status_icons_visibility": "Indicateur de la confidentialité du post",
+  "settings.swipe_to_change_columns": "Glissement latéral pour changer de colonne (mobile uniquement)",
+  "settings.tag_misleading_links": "Étiqueter les liens trompeurs",
+  "settings.tag_misleading_links.hint": "Ajouter une indication visuelle avec l'hôte cible du lien à chaque lien ne le mentionnant pas explicitement",
+  "settings.wide_view": "Vue élargie (mode ordinateur uniquement)",
+  "settings.wide_view_hint": "Étire les colonnes pour mieux remplir l'espace disponible.",
+  "status.collapse": "Replier",
+  "status.has_audio": "Contient des fichiers audio attachés",
+  "status.has_pictures": "Contient des images attachées",
+  "status.has_preview_card": "Contient une carte de prévisualisation attachée",
+  "status.has_video": "Contient des vidéos attachées",
+  "status.in_reply_to": "Ce post est une réponse",
+  "status.is_poll": "Ce post est un sondage",
+  "status.local_only": "Visible uniquement depuis votre instance",
+  "status.sensitive_toggle": "Cliquer pour voir",
+  "status.uncollapse": "Déplier",
+  "web_app_crash.change_your_settings": "Changez vos {settings}",
+  "web_app_crash.content": "Voici les différentes options qui s'offrent à vous :",
+  "web_app_crash.debug_info": "Informations de débogage",
+  "web_app_crash.disable_addons": "Désactivez les extensions de votre navigateur, ainsi que les outils de traduction intégrés",
+  "web_app_crash.issue_tracker": "traqueur d'erreurs",
+  "web_app_crash.reload": "Rafraichir",
+  "web_app_crash.reload_page": "{reload} la page actuelle",
+  "web_app_crash.report_issue": "Signalez un bug dans le {issuetracker}",
+  "web_app_crash.settings": "paramètres",
+  "web_app_crash.title": "Nous sommes navrés, mais quelque chose s'est mal passé dans l'application Mastodon."
+}
diff --git a/app/javascript/flavours/glitch/locales/fy.json b/app/javascript/flavours/glitch/locales/fy.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/fy.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/ga.js b/app/javascript/flavours/glitch/locales/ga.js
deleted file mode 100644
index af2846ff8..000000000
--- a/app/javascript/flavours/glitch/locales/ga.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ga.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ga.json b/app/javascript/flavours/glitch/locales/ga.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ga.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/gd.js b/app/javascript/flavours/glitch/locales/gd.js
deleted file mode 100644
index 604ee86dc..000000000
--- a/app/javascript/flavours/glitch/locales/gd.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/gd.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/gd.json b/app/javascript/flavours/glitch/locales/gd.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/gd.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/gl.js b/app/javascript/flavours/glitch/locales/gl.js
deleted file mode 100644
index 6a9140b1a..000000000
--- a/app/javascript/flavours/glitch/locales/gl.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/gl.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/gl.json b/app/javascript/flavours/glitch/locales/gl.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/gl.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/he.js b/app/javascript/flavours/glitch/locales/he.js
deleted file mode 100644
index 99516ee0c..000000000
--- a/app/javascript/flavours/glitch/locales/he.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/he.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/he.json b/app/javascript/flavours/glitch/locales/he.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/he.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/hi.js b/app/javascript/flavours/glitch/locales/hi.js
deleted file mode 100644
index 1a569495f..000000000
--- a/app/javascript/flavours/glitch/locales/hi.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/hi.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hi.json b/app/javascript/flavours/glitch/locales/hi.json
new file mode 100644
index 000000000..f6eb75f84
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hi.json
@@ -0,0 +1,18 @@
+{
+  "about.fork_disclaimer": "ग्लिच-सोक एक मुफ्त और ओपन सोर्स सॉफ़्टवेर है जो मैस्टोडॉन से फोर्क किया गया है",
+  "account.add_account_note": "@{name} के लिए कोई नोट लिखें",
+  "account.follows": "फ़ॉलोज़",
+  "account.joined": "ज़ोईन करने की {date}",
+  "account.suspended_disclaimer_full": "यह यूज़र एक मॉडरेटर द्वारा सस्पेंड कर दिया गया है",
+  "account.view_full_profile": "पूरी प्रोफ़ाइल देखें",
+  "account_note.cancel": "कैन्सल",
+  "account_note.edit": "एडिट या सम्पादन करें",
+  "account_note.glitch_placeholder": "कोई कॉमेंट नहीं दिया गया है",
+  "account_note.save": "सेव",
+  "advanced_options.icon_title": "एडवांस्ड ऑप्शन्स",
+  "advanced_options.local-only.long": "दूसरे इंस्टेंसों में पोस्ट ना करें",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/hr.js b/app/javascript/flavours/glitch/locales/hr.js
deleted file mode 100644
index dbf9b4b9f..000000000
--- a/app/javascript/flavours/glitch/locales/hr.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/hr.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hr.json b/app/javascript/flavours/glitch/locales/hr.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hr.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/hu.js b/app/javascript/flavours/glitch/locales/hu.js
deleted file mode 100644
index 1f0849af3..000000000
--- a/app/javascript/flavours/glitch/locales/hu.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/hu.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hu.json b/app/javascript/flavours/glitch/locales/hu.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hu.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/hy.js b/app/javascript/flavours/glitch/locales/hy.js
deleted file mode 100644
index 96f6a4d19..000000000
--- a/app/javascript/flavours/glitch/locales/hy.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/hy.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/hy.json b/app/javascript/flavours/glitch/locales/hy.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/hy.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/id.js b/app/javascript/flavours/glitch/locales/id.js
deleted file mode 100644
index 07e5f7e56..000000000
--- a/app/javascript/flavours/glitch/locales/id.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/id.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/id.json b/app/javascript/flavours/glitch/locales/id.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/id.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ig.json b/app/javascript/flavours/glitch/locales/ig.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ig.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/io.js b/app/javascript/flavours/glitch/locales/io.js
deleted file mode 100644
index 74ea6fae6..000000000
--- a/app/javascript/flavours/glitch/locales/io.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/io.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/io.json b/app/javascript/flavours/glitch/locales/io.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/io.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/is.js b/app/javascript/flavours/glitch/locales/is.js
deleted file mode 100644
index b05a08ad0..000000000
--- a/app/javascript/flavours/glitch/locales/is.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/is.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/is.json b/app/javascript/flavours/glitch/locales/is.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/is.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/it.js b/app/javascript/flavours/glitch/locales/it.js
deleted file mode 100644
index 90f543093..000000000
--- a/app/javascript/flavours/glitch/locales/it.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/it.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/it.json b/app/javascript/flavours/glitch/locales/it.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/it.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ja.js b/app/javascript/flavours/glitch/locales/ja.js
deleted file mode 100644
index 52aeed3d6..000000000
--- a/app/javascript/flavours/glitch/locales/ja.js
+++ /dev/null
@@ -1,158 +0,0 @@
-import inherited from 'mastodon/locales/ja.json';
-
-const messages = {
-  'getting_started.open_source_notice': 'Glitchsocは{Mastodon}によるフリーなオープンソースソフトウェアです。誰でもGitHub({github})から開発に參加したり、問題を報告したりできます。',
-  'layout.auto': '自動',
-  'layout.current_is': 'あなたの現在のレイアウト:',
-  'layout.desktop': 'デスクトップ',
-  'layout.single': 'モバイル',
-  'navigation_bar.app_settings': 'アプリ設定',
-  'navigation_bar.featured_users': '紹介しているアカウント',
-  'navigation_bar.misc': 'その他',
-  'getting_started.onboarding': '解説を表示',
-  'onboarding.page_one.federation': '{domain}はMastodonのインスタンスです。Mastodonとは、独立したサーバが連携して作るソーシャルネットワークです。これらのサーバーをインスタンスと呼びます。',
-  'onboarding.page_one.welcome': '{domain}へようこそ!',
-  'onboarding.page_six.github': '{domain}はGlitchsocを使用しています。Glitchsocは{Mastodon}のフレンドリーな{fork}で、どんなMastodonアプリやインスタンスとも互換性があります。Glitchsocは完全に無料で、オープンソースです。{github}でバグ報告や機能要望あるいは貢獻をすることが可能です。',
-  'settings.always_show_spoilers_field': '常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)',
-  'settings.auto_collapse': '自動折りたたみ',
-  'settings.auto_collapse_all': 'すべて',
-  'settings.auto_collapse_lengthy': '長いトゥート',
-  'settings.auto_collapse_media': 'メディア付きトゥート',
-  'settings.auto_collapse_notifications': '通知',
-  'settings.auto_collapse_reblogs': 'ブースト',
-  'settings.auto_collapse_replies': '返信',
-  'settings.close': '閉じる',
-  'settings.collapsed_statuses': 'トゥート折りたたみ',
-  'settings.confirm_missing_media_description': '画像に対する補助記載がないときに投稿前の警告を表示する',
-  'settings.content_warnings': 'コンテンツワーニング',
-  'settings.content_warnings_filter': '説明に指定した文字が含まれているものを自動で展開しないようにする',
-  'settings.content_warnings.regexp': '正規表現',
-  'settings.enable_collapsed': 'トゥート折りたたみを有効にする',
-  'settings.enable_content_warnings_auto_unfold': 'コンテンツワーニング指定されている投稿を常に表示する',
-  'settings.general': '一般',
-  'settings.image_backgrounds': '画像背景',
-  'settings.image_backgrounds_media': '折りたまれたメディア付きトゥートをプレビュー',
-  'settings.image_backgrounds_users': '折りたまれたトゥートの背景を変更する',
-  'settings.media': 'メディア',
-  'settings.media_letterbox': 'メディアをレターボックス式で表示',
-  'settings.media_fullwidth': '全幅メディアプレビュー',
-  'settings.navbar_under': 'ナビを画面下部に移動させる(モバイル レイアウトのみ)',
-  'settings.notifications.favicon_badge': '通知アイコンに未読件数を表示する',
-  'settings.notifications_opts': '通知の設定',
-  'settings.notifications.tab_badge': '未読の通知があるとき、通知アイコンにマークを表示する',
-  'settings.preferences': 'ユーザー設定',
-  'settings.wide_view': 'ワイドビュー(デスクトップ レイアウトのみ)',
-  'settings.compose_box_opts': 'コンポーズボックス設定',
-  'settings.show_reply_counter': '投稿に対するリプライの数を表示する',
-  'settings.side_arm': 'セカンダリートゥートボタン',
-  'settings.side_arm.none': '表示しない',
-  'settings.side_arm_reply_mode': '返信時の投稿範囲',
-  'settings.side_arm_reply_mode.copy': '返信先の投稿範囲を利用する',
-  'settings.side_arm_reply_mode.keep': 'セカンダリートゥートボタンの設定を維持する',
-  'settings.side_arm_reply_mode.restrict': '返信先の投稿範囲に制限する',
-  'settings.layout': 'レイアウト',
-  'settings.layout_opts': 'レイアウトの設定',
-  'status.collapse': '折りたたむ',
-  'status.uncollapse': '折りたたみを解除',
-
-  'confirmations.missing_media_description.message': '少なくとも1つの画像に視覚障害者のための画像説明が付与されていません。すべての画像に対して説明を付与することを望みます。',
-  'confirmations.missing_media_description.confirm': 'このまま投稿',
-  'confirmations.missing_media_description.edit': 'メディアを編集',
-
-  'favourite_modal.combo': '次からは {combo} を押せば、これをスキップできます。',
-
-  'home.column_settings.show_direct': 'DMを表示',
-  'home.column_settings.advanced': '高度',
-  'home.column_settings.filter_regex': '正規表現でフィルター',
-
-  'notification.markForDeletion': '選択',
-  'notifications.clear': '通知を全てクリアする',
-  'notifications.marked_clear_confirmation': '削除した全ての通知を完全に削除してもよろしいですか?',
-  'notifications.marked_clear': '選択した通知を削除する',
-
-  'notification_purge.btn_all': 'すべて\n選択',
-  'notification_purge.btn_none': '選択\n解除',
-  'notification_purge.btn_invert': '選択を\n反転',
-  'notification_purge.btn_apply': '選択したものを\n削除',
-
-  'compose.attach.upload': 'ファイルをアップロード',
-  'compose.attach.doodle': 'お絵描きをする',
-  'compose.attach': '添付...',
-
-  'advanced_options.local-only.short': 'ローカル限定',
-  'advanced_options.local-only.long': '他のインスタンスには投稿されません',
-  'advanced_options.local-only.tooltip': 'この投稿はローカル限定投稿です',
-  'advanced_options.icon_title': '高度な設定',
-  'advanced_options.threaded_mode.short': 'スレッドモード',
-  'advanced_options.threaded_mode.long': '投稿時に自動的に返信するように設定します',
-  'advanced_options.threaded_mode.tooltip': 'スレッドモードを有効にする',
-
-  'navigation_bar.direct': 'ダイレクトメッセージ',
-  'navigation_bar.bookmarks': 'ブックマーク',
-  'column.bookmarks': 'ブックマーク',
-
-  'account.add_account_note': '@{name}のメモを追加',
-  'account.disclaimer_full': 'このユーザー情報は不正確な可能性があります。',
-  'account.follows': 'フォロー',
-  'account.suspended_disclaimer_full': 'このユーザーはモデレータにより停止されました。',
-  'account.view_full_profile': '正確な情報を見る',
-  'account_note.cancel': 'キャンセル',
-  'account_note.edit': '編集',
-  'account_note.glitch_placeholder': 'メモがありません',
-  'account_note.save': '保存',
-  'boost_modal.missing_description': 'このトゥートには少なくとも1つの画像に説明が付与されていません',
-  'community.column_settings.allow_local_only': 'ローカル限定投稿を表示する',
-  'compose.content-type.html': 'HTML',
-  'compose.content-type.markdown': 'マークダウン',
-  'compose.content-type.plain': 'プレーンテキスト',
-  'compose_form.poll.multiple_choices': '複数回答を許可',
-  'compose_form.poll.single_choice': '単一回答を許可',
-  'compose_form.spoiler': '本文は警告の後ろに隠す',
-  'confirmation_modal.do_not_ask_again': 'もう1度尋ねない',
-  'confirmations.discard_edit_media.confirm': '破棄',
-  'confirmations.discard_edit_media.message': 'メディアの説明・プレビューに保存していない変更があります。破棄してもよろしいですか?',
-  'confirmations.unfilter': 'このフィルターされたトゥートについての情報',
-  'confirmations.unfilter.author': '筆者',
-  'confirmations.unfilter.confirm': '見る',
-  'confirmations.unfilter.edit_filter': 'フィルターを編集',
-  'confirmations.unfilter.filters': '適用されたフィルター',
-  'content-type.change': 'コンテンツ形式を変更',
-  'direct.conversations_mode': '会話',
-  'direct.timeline_mode': 'タイムライン',
-  'endorsed_accounts_editor.endorsed_accounts': '紹介しているユーザー',
-  'keyboard_shortcuts.bookmark': 'ブックマーク',
-  'keyboard_shortcuts.secondary_toot': 'セカンダリートゥートの公開範囲でトゥートする',
-  'keyboard_shortcuts.toggle_collapse': '折りたたむ/折りたたみを解除',
-  'moved_to_warning': 'このアカウント{moved_to_link}に引っ越したため、新しいフォロワーを受け入れていません。',
-  'settings.show_action_bar': 'アクションバーを表示',
-  'settings.filtering_behavior': 'フィルターの振る舞い',
-  'settings.filtering_behavior.cw': '警告文にフィルターされた単語を付加して表示します',
-  'settings.filtering_behavior.drop': 'フィルターされたトゥートを完全に隠します',
-  'settings.filtering_behavior.hide': '\'フィルターされました\'とその理由を確認するボタンを表示する',
-  'settings.filtering_behavior.upstream': '\'フィルターされました\'とバニラMastodonと同じように表示する',
-  'settings.filters': 'フィルター',
-  'settings.hicolor_privacy_icons': 'ハイカラーの公開範囲アイコン',
-  'settings.hicolor_privacy_icons.hint': '公開範囲アイコンを明るく表示し見分けやすい色にします',
-  'settings.confirm_boost_missing_media_description': 'メディアの説明が欠けているトゥートをブーストする前に確認ダイアログを表示する',
-  'settings.tag_misleading_links': '誤解を招くリンクにタグをつける',
-  'settings.tag_misleading_links.hint': '明示的に言及していないすべてのリンクに、リンクターゲットホストを含む視覚的な表示を追加します',
-  'settings.rewrite_mentions': '表示されたトゥートの返信先表示を書き換える',
-  'settings.rewrite_mentions_acct': 'ユーザー名とドメイン名(アカウントがリモートの場合)を表示するように書き換える',
-  'settings.rewrite_mentions_no': '書き換えない',
-  'settings.rewrite_mentions_username': 'ユーザー名を表示するように書き換える',
-  'settings.swipe_to_change_columns': 'スワイプでカラムを切り替え可能にする(モバイルのみ)',
-  'settings.prepend_cw_re': '返信するとき警告に "re: "を付加する',
-  'settings.preselect_on_reply': '返信するときユーザー名を事前選択する',
-  'settings.confirm_before_clearing_draft': '作成しているメッセージが上書きされる前に確認ダイアログを表示する',
-  'settings.show_content_type_choice': 'トゥートを書くときコンテンツ形式の選択ボタンを表示する',
-  'settings.inline_preview_cards': '外部リンクに埋め込みプレビューを有効にする',
-  'settings.media_reveal_behind_cw': '既定で警告指定されているトゥートの閲覧注意メディアを表示する',
-  'settings.pop_in_left': '左',
-  'settings.pop_in_player': 'ポップインプレイヤーを有効化する',
-  'settings.pop_in_position': 'ポップインプレーヤーの位置:',
-  'settings.pop_in_right': '右',
-  'status.show_filter_reason': '(理由を見る)',
-
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ja.json b/app/javascript/flavours/glitch/locales/ja.json
new file mode 100644
index 000000000..610cd7525
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ja.json
@@ -0,0 +1,124 @@
+{
+  "account.add_account_note": "@{name}のメモを追加",
+  "account.disclaimer_full": "このユーザー情報は不正確な可能性があります。",
+  "account.follows": "フォロー",
+  "account.suspended_disclaimer_full": "このユーザーはモデレータにより停止されました。",
+  "account.view_full_profile": "正確な情報を見る",
+  "account_note.cancel": "キャンセル",
+  "account_note.edit": "編集",
+  "account_note.glitch_placeholder": "メモがありません",
+  "account_note.save": "保存",
+  "advanced_options.icon_title": "高度な設定",
+  "advanced_options.local-only.long": "他のインスタンスには投稿されません",
+  "advanced_options.local-only.short": "ローカル限定",
+  "advanced_options.local-only.tooltip": "この投稿はローカル限定投稿です",
+  "advanced_options.threaded_mode.long": "投稿時に自動的に返信するように設定します",
+  "advanced_options.threaded_mode.short": "スレッドモード",
+  "advanced_options.threaded_mode.tooltip": "スレッドモードを有効にする",
+  "boost_modal.missing_description": "このトゥートには少なくとも1つの画像に説明が付与されていません",
+  "community.column_settings.allow_local_only": "ローカル限定投稿を表示する",
+  "compose.attach": "添付...",
+  "compose.attach.doodle": "お絵描きをする",
+  "compose.attach.upload": "ファイルをアップロード",
+  "compose.content-type.markdown": "マークダウン",
+  "compose.content-type.plain": "プレーンテキスト",
+  "compose_form.poll.multiple_choices": "複数回答を許可",
+  "compose_form.poll.single_choice": "単一回答を許可",
+  "compose_form.spoiler": "本文は警告の後ろに隠す",
+  "confirmation_modal.do_not_ask_again": "もう1度尋ねない",
+  "confirmations.missing_media_description.confirm": "このまま投稿",
+  "confirmations.missing_media_description.edit": "メディアを編集",
+  "confirmations.missing_media_description.message": "少なくとも1つの画像に視覚障害者のための画像説明が付与されていません。すべての画像に対して説明を付与することを望みます。",
+  "confirmations.unfilter.author": "筆者",
+  "confirmations.unfilter.confirm": "見る",
+  "confirmations.unfilter.edit_filter": "フィルターを編集",
+  "confirmations.unfilter.filters": "適用されたフィルター",
+  "content-type.change": "コンテンツ形式を変更",
+  "endorsed_accounts_editor.endorsed_accounts": "紹介しているユーザー",
+  "favourite_modal.combo": "次からは {combo} を押せば、これをスキップできます。",
+  "getting_started.onboarding": "解説を表示",
+  "home.column_settings.advanced": "高度",
+  "home.column_settings.filter_regex": "正規表現でフィルター",
+  "home.column_settings.show_direct": "DMを表示",
+  "keyboard_shortcuts.bookmark": "ブックマーク",
+  "keyboard_shortcuts.secondary_toot": "セカンダリートゥートの公開範囲でトゥートする",
+  "keyboard_shortcuts.toggle_collapse": "折りたたむ/折りたたみを解除",
+  "layout.auto": "自動",
+  "layout.desktop": "デスクトップ",
+  "layout.single": "モバイル",
+  "moved_to_warning": "このアカウント{moved_to_link}に引っ越したため、新しいフォロワーを受け入れていません。",
+  "navigation_bar.app_settings": "アプリ設定",
+  "navigation_bar.featured_users": "紹介しているアカウント",
+  "navigation_bar.misc": "その他",
+  "notification.markForDeletion": "選択",
+  "notification_purge.btn_all": "すべて\n選択",
+  "notification_purge.btn_apply": "選択したものを\n削除",
+  "notification_purge.btn_invert": "選択を\n反転",
+  "notification_purge.btn_none": "選択\n解除",
+  "notifications.marked_clear": "選択した通知を削除する",
+  "notifications.marked_clear_confirmation": "削除した全ての通知を完全に削除してもよろしいですか?",
+  "onboarding.page_one.federation": "{domain}はMastodonのインスタンスです。Mastodonとは、独立したサーバが連携して作るソーシャルネットワークです。これらのサーバーをインスタンスと呼びます。",
+  "onboarding.page_one.welcome": "{domain}へようこそ!",
+  "onboarding.page_six.github": "{domain}はGlitchsocを使用しています。Glitchsocは{Mastodon}のフレンドリーな{fork}で、どんなMastodonアプリやインスタンスとも互換性があります。Glitchsocは完全に無料で、オープンソースです。{github}でバグ報告や機能要望あるいは貢獻をすることが可能です。",
+  "settings.always_show_spoilers_field": "常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)",
+  "settings.auto_collapse": "自動折りたたみ",
+  "settings.auto_collapse_all": "すべて",
+  "settings.auto_collapse_lengthy": "長いトゥート",
+  "settings.auto_collapse_media": "メディア付きトゥート",
+  "settings.auto_collapse_notifications": "通知",
+  "settings.auto_collapse_reblogs": "ブースト",
+  "settings.auto_collapse_replies": "返信",
+  "settings.close": "閉じる",
+  "settings.collapsed_statuses": "トゥート折りたたみ",
+  "settings.compose_box_opts": "コンポーズボックス設定",
+  "settings.confirm_before_clearing_draft": "作成しているメッセージが上書きされる前に確認ダイアログを表示する",
+  "settings.confirm_boost_missing_media_description": "メディアの説明が欠けているトゥートをブーストする前に確認ダイアログを表示する",
+  "settings.confirm_missing_media_description": "画像に対する補助記載がないときに投稿前の警告を表示する",
+  "settings.content_warnings": "コンテンツワーニング",
+  "settings.content_warnings.regexp": "正規表現",
+  "settings.content_warnings_filter": "説明に指定した文字が含まれているものを自動で展開しないようにする",
+  "settings.enable_collapsed": "トゥート折りたたみを有効にする",
+  "settings.enable_content_warnings_auto_unfold": "コンテンツワーニング指定されている投稿を常に表示する",
+  "settings.general": "一般",
+  "settings.hicolor_privacy_icons": "ハイカラーの公開範囲アイコン",
+  "settings.hicolor_privacy_icons.hint": "公開範囲アイコンを明るく表示し見分けやすい色にします",
+  "settings.image_backgrounds": "画像背景",
+  "settings.image_backgrounds_media": "折りたまれたメディア付きトゥートをプレビュー",
+  "settings.image_backgrounds_users": "折りたまれたトゥートの背景を変更する",
+  "settings.inline_preview_cards": "外部リンクに埋め込みプレビューを有効にする",
+  "settings.layout": "レイアウト",
+  "settings.layout_opts": "レイアウトの設定",
+  "settings.media": "メディア",
+  "settings.media_fullwidth": "全幅メディアプレビュー",
+  "settings.media_letterbox": "メディアをレターボックス式で表示",
+  "settings.media_reveal_behind_cw": "既定で警告指定されているトゥートの閲覧注意メディアを表示する",
+  "settings.notifications.favicon_badge": "通知アイコンに未読件数を表示する",
+  "settings.notifications.tab_badge": "未読の通知があるとき、通知アイコンにマークを表示する",
+  "settings.notifications_opts": "通知の設定",
+  "settings.pop_in_left": "左",
+  "settings.pop_in_player": "ポップインプレイヤーを有効化する",
+  "settings.pop_in_position": "ポップインプレーヤーの位置:",
+  "settings.pop_in_right": "右",
+  "settings.preferences": "ユーザー設定",
+  "settings.prepend_cw_re": "返信するとき警告に \"re: \"を付加する",
+  "settings.preselect_on_reply": "返信するときユーザー名を事前選択する",
+  "settings.rewrite_mentions": "表示されたトゥートの返信先表示を書き換える",
+  "settings.rewrite_mentions_acct": "ユーザー名とドメイン名(アカウントがリモートの場合)を表示するように書き換える",
+  "settings.rewrite_mentions_no": "書き換えない",
+  "settings.rewrite_mentions_username": "ユーザー名を表示するように書き換える",
+  "settings.show_action_bar": "アクションバーを表示",
+  "settings.show_content_type_choice": "トゥートを書くときコンテンツ形式の選択ボタンを表示する",
+  "settings.show_reply_counter": "投稿に対するリプライの数を表示する",
+  "settings.side_arm": "セカンダリートゥートボタン",
+  "settings.side_arm.none": "表示しない",
+  "settings.side_arm_reply_mode": "返信時の投稿範囲",
+  "settings.side_arm_reply_mode.copy": "返信先の投稿範囲を利用する",
+  "settings.side_arm_reply_mode.keep": "セカンダリートゥートボタンの設定を維持する",
+  "settings.side_arm_reply_mode.restrict": "返信先の投稿範囲に制限する",
+  "settings.swipe_to_change_columns": "スワイプでカラムを切り替え可能にする(モバイルのみ)",
+  "settings.tag_misleading_links": "誤解を招くリンクにタグをつける",
+  "settings.tag_misleading_links.hint": "明示的に言及していないすべてのリンクに、リンクターゲットホストを含む視覚的な表示を追加します",
+  "settings.wide_view": "ワイドビュー(デスクトップ レイアウトのみ)",
+  "status.collapse": "折りたたむ",
+  "status.uncollapse": "折りたたみを解除"
+}
diff --git a/app/javascript/flavours/glitch/locales/ka.js b/app/javascript/flavours/glitch/locales/ka.js
deleted file mode 100644
index 3e06f4282..000000000
--- a/app/javascript/flavours/glitch/locales/ka.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ka.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ka.json b/app/javascript/flavours/glitch/locales/ka.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ka.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/kab.js b/app/javascript/flavours/glitch/locales/kab.js
deleted file mode 100644
index 5ed1156ef..000000000
--- a/app/javascript/flavours/glitch/locales/kab.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/kab.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kab.json b/app/javascript/flavours/glitch/locales/kab.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kab.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/kk.js b/app/javascript/flavours/glitch/locales/kk.js
deleted file mode 100644
index 8d00fb035..000000000
--- a/app/javascript/flavours/glitch/locales/kk.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/kk.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kk.json b/app/javascript/flavours/glitch/locales/kk.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kk.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/kn.js b/app/javascript/flavours/glitch/locales/kn.js
deleted file mode 100644
index 1c50e3628..000000000
--- a/app/javascript/flavours/glitch/locales/kn.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/kn.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kn.json b/app/javascript/flavours/glitch/locales/kn.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kn.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ko.js b/app/javascript/flavours/glitch/locales/ko.js
deleted file mode 100644
index a817044c1..000000000
--- a/app/javascript/flavours/glitch/locales/ko.js
+++ /dev/null
@@ -1,208 +0,0 @@
-import inherited from 'mastodon/locales/ko.json';
-
-const messages = {
-  'account.add_account_note': '@{name} 님에 대한 메모 추가',
-  'account.disclaimer_full': '아래에 있는 정보들은 사용자의 프로필을 완벽하게 나타내지 못하고 있을 수도 있습니다.',
-  'account.follows': '팔로우',
-  'account.suspended_disclaimer_full': '이 사용자는 중재자에 의해 정지되었습니다.',
-  'account.view_full_profile': '전체 프로필 보기',
-  'account_note.cancel': '취소',
-  'account_note.edit': '편집',
-  'account_note.glitch_placeholder': '코멘트가 없습니다',
-  'account_note.save': '저장',
-  'advanced_options.icon_title': '고급 옵션',
-  'advanced_options.local-only.long': '다른 서버에 게시하지 않기',
-  'advanced_options.local-only.short': '로컬 전용',
-  'advanced_options.local-only.tooltip': '이 게시물은 로컬 전용입니다',
-  'advanced_options.threaded_mode.long': '글을 작성하고 자동으로 답글 열기',
-  'advanced_options.threaded_mode.short': '글타래 모드',
-  'advanced_options.threaded_mode.tooltip': '글타래 모드 활성화됨',
-  'boost_modal.missing_description': '이 게시물은 설명이 없는 미디어를 포함하고 있습니다',
-  'column.favourited_by': '즐겨찾기 한 사람',
-  'column.heading': '기타',
-  'column.reblogged_by': '부스트 한 사람',
-  'column.subheading': '다양한 옵션',
-  'column.toot': '게시물과 답글',
-  'column_header.profile': '프로필',
-  'column_subheading.lists': '리스트',
-  'column_subheading.navigation': '탐색',
-  'community.column_settings.allow_local_only': '로컬 전용 글 보기',
-  'compose.attach': '첨부…',
-  'compose.attach.doodle': '뭔가 그려보세요',
-  'compose.attach.upload': '파일 업로드',
-  'compose.content-type.html': 'HTML',
-  'compose.content-type.markdown': '마크다운',
-  'compose.content-type.plain': '일반 텍스트',
-  'compose_form.poll.multiple_choices': '여러 개 선택 가능',
-  'compose_form.poll.single_choice': '하나만 선택 가능',
-  'compose_form.spoiler': '경고 메시지로 숨기기',
-  'confirmation_modal.do_not_ask_again': '다음부터 확인창을 띄우지 않기',
-  'confirmations.discard_edit_media.message': '저장하지 않은 미디어 설명이나 미리보기가 있습니다, 그냥 닫을까요?',
-  'confirmations.missing_media_description.confirm': '그냥 보내기',
-  'confirmations.missing_media_description.edit': '미디어 편집',
-  'confirmations.missing_media_description.message': '하나 이상의 미디어에 대해 설명을 작성하지 않았습니다. 시각장애인을 위해 모든 미디어에 설명을 추가하는 것을 고려해주세요.',
-  'confirmations.unfilter': '이 필터링 된 글에 대한 정보',
-  'confirmations.unfilter.author': '작성자',
-  'confirmations.unfilter.confirm': '보기',
-  'confirmations.unfilter.edit_filter': '필터 편집',
-  'confirmations.unfilter.filters': '적용된 {count, plural, one {필터} other {필터들}}',
-  'content-type.change': '콘텐트 타입',
-  'direct.conversations_mode': '대화',
-  'direct.timeline_mode': '타임라인',
-  'endorsed_accounts_editor.endorsed_accounts': '추천하는 계정들',
-  'favourite_modal.combo': '다음엔 {combo}를 눌러 건너뛸 수 있습니다',
-  'getting_started.onboarding': '둘러보기',
-  'getting_started.open_source_notice': '글리치는 {Mastodon}의 자유 오픈소스 포크버전입니다. {github}에서 문제를 리포팅 하거나 기여를 할 수 있습니다.',
-  'home.column_settings.advanced': '고급',
-  'home.column_settings.filter_regex': '정규표현식으로 필터',
-  'home.column_settings.show_direct': 'DM 보여주기',
-  'home.settings': '컬럼 설정',
-  'keyboard_shortcuts.bookmark': '북마크',
-  'keyboard_shortcuts.secondary_toot': '보조 프라이버시 설정으로 글 보내기',
-  'keyboard_shortcuts.toggle_collapse': '글 접거나 펼치기',
-  'layout.auto': '자동',
-  'layout.current_is': '현재 레이아웃:',
-  'layout.desktop': '데스크탑',
-  'layout.hint.auto': '“고급 웹 인터페이스 활성화” 설정과 화면 크기에 따라 자동으로 레이아웃을 고릅니다.',
-  'layout.hint.desktop': '“고급 웹 인터페이스 활성화” 설정이나 화면 크기에 관계 없이 멀티 컬럼 레이아웃을 사용합니다.',
-  'layout.hint.single': '“고급 웹 인터페이스 활성화” 설정이나 화면 크기에 관계 없이 싱글 컬럼 레이아웃을 사용합니다.',
-  'layout.single': '모바일',
-  'media_gallery.sensitive': '민감함',
-  'moved_to_warning': '이 계정은 {moved_to_link}로 이동한 것으로 표시되었고, 새 팔로우를 받지 않는 것 같습니다.',
-  'navigation_bar.app_settings': '앱 설정',
-  'navigation_bar.featured_users': '추천된 계정들',
-  'navigation_bar.misc': '다양한 옵션들',
-  'notification.markForDeletion': '삭제하기 위해 표시',
-  'notification_purge.btn_all': '전체선택',
-  'notification_purge.btn_apply': '선택된 알림 삭제',
-  'notification_purge.btn_invert': '선택반전',
-  'notification_purge.btn_none': '전체선택해제',
-  'notification_purge.start': '알림 삭제모드로 들어가기',
-  'notifications.clear': '내 알림 모두 지우기',
-  'notifications.marked_clear': '선택된 알림 모두 삭제',
-  'notifications.marked_clear_confirmation': '정말로 선택된 알림들을 영구적으로 삭제할까요?',
-  'onboarding.done': '완료',
-  'onboarding.next': '다음',
-  'onboarding.page_five.public_timelines': '로컬 타임라인은 {domain}에 있는 모든 사람의 공개글을 보여줍니다. 연합 타임라인은 {domain}에 있는 사람들이 팔로우 하는 모든 사람의 공개글을 보여줍니다. 이것들은 공개 타임라인이라고 불리며, 새로운 사람들을 발견할 수 있는 좋은 방법입니다.',
-  'onboarding.page_four.home': '홈 타임라인은 당신이 팔로우 한 사람들의 글을 보여줍니다.',
-  'onboarding.page_four.notifications': '알림 컬럼은 누군가가 당신과 상호작용한 것들을 보여줍니다.',
-  'onboarding.page_one.federation': '{domain}은 마스토돈의 \'인스턴스\'입니다. 마스토돈은 하나의 거대한 소셜 네트워크를 만들기 위해 참여한 서버들의 네트워크입니다. 우린 이 서버들을 인스턴스라고 부릅니다.',
-  'onboarding.page_one.handle': '당신은 {domain}에 속해 있으며, 전체 핸들은 {handle} 입니다.',
-  'onboarding.page_one.welcome': '{domain}에 오신 것을 환영합니다!',
-  'onboarding.page_six.admin': '우리 서버의 관리자는 {admin} 님입니다.',
-  'onboarding.page_six.almost_done': '거의 다 되었습니다…',
-  'onboarding.page_six.appetoot': '본 아페툿!',
-  'onboarding.page_six.apps_available': 'iOS, 안드로이드, 그리고 다른 플랫폼들을 위한 {apps}이 존재합니다.',
-  'onboarding.page_six.github': '{domain}은 글리치를 통해 구동 됩니다. 글리치는 {Mastodon}의 {fork}입니다, 그리고 어떤 마스토돈 인스턴스나 앱과도 호환 됩니다. 글리치는 완전한 자유 오픈소스입니다. {github}에서 버그를 리포팅 하거나, 기능을 제안하거나, 코드를 기여할 수 있습니다.',
-  'onboarding.page_six.guidelines': '커뮤니티 가이드라인',
-  'onboarding.page_six.read_guidelines': '{domain}의 {guidelines}을 읽어주세요!',
-  'onboarding.page_six.various_app': '모바일 앱',
-  'onboarding.page_three.profile': '프로필을 수정해 아바타, 바이오, 표시되는 이름을 설정하세요. 거기에서 다른 설정들도 찾을 수 있습니다.',
-  'onboarding.page_three.search': '검색창을 사용해 사람들과 해시태그를 찾아보세요. 예를 들면 {illustration}이라든지 {introcustions} 같은 것으로요. 이 인스턴스에 있지 않은 사람을 찾으려면, 전체 핸들을 사용하세요.',
-  'onboarding.page_two.compose': '작성 컬럼에서 게시물을 작성하세요. 그림을 업로드 할 수 있고, 공개설정을 바꿀 수도 있으며, 아래 아이콘을 통해 열람주의 텍스트를 설정할 수 있습니다.',
-  'onboarding.skip': '건너뛰기',
-  'settings.always_show_spoilers_field': '열람주의 항목을 언제나 활성화',
-  'settings.auto_collapse': '자동으로 접기',
-  'settings.auto_collapse_all': '모두',
-  'settings.auto_collapse_lengthy': '긴 글',
-  'settings.auto_collapse_media': '미디어 포함 글',
-  'settings.auto_collapse_notifications': '알림',
-  'settings.auto_collapse_reblogs': '부스트',
-  'settings.auto_collapse_replies': '답글',
-  'settings.close': '닫기',
-  'settings.collapsed_statuses': '접힌 글',
-  'settings.compose_box_opts': '작성 상자',
-  'settings.confirm_before_clearing_draft': '작성 중인 메시지를 덮어씌우기 전에 확인창을 보여주기',
-  'settings.confirm_boost_missing_media_description': '미디어 설명이 없는 글을 부스트하려 할 때 확인창을 보여주기',
-  'settings.confirm_missing_media_description': '미디어 설명이 없는 글을 작성하려 할 때 확인창을 보여주기',
-  'settings.content_warnings': '열람주의',
-  'settings.content_warnings.regexp': '정규표현식',
-  'settings.content_warnings_filter': '자동으로 펼치지 않을 열람주의 문구:',
-  'settings.deprecated_setting': '이 설정은 마스토돈의 {settings_page_link}에서 관리됩니다',
-  'settings.enable_collapsed': '접힌 글 활성화',
-  'settings.enable_content_warnings_auto_unfold': '자동으로 열람주의 펼치기',
-  'settings.filtering_behavior': '필터링 동작',
-  'settings.filtering_behavior.cw': '게시물을 보여주되, 필터된 단어를 열람주의에 추가합니다',
-  'settings.filtering_behavior.drop': '완전히 숨깁니다',
-  'settings.filtering_behavior.hide': '\'필터됨\'이라고 표시하고 이유를 표시하는 버튼을 추가합니다',
-  'settings.filtering_behavior.upstream': '\'필터됨\'이라고 일반 마스토돈처럼 표시합니다',
-  'settings.filters': '필터',
-  'settings.general': '일반',
-  'settings.hicolor_privacy_icons': '높은 채도의 공개설정 아이콘',
-  'settings.hicolor_privacy_icons.hint': '공개설정 아이콘들을 밝고 구분하기 쉬운 색으로 표시합니다',
-  'settings.image_backgrounds': '이미지 배경',
-  'settings.image_backgrounds_media': '접힌 글의 미디어 미리보기',
-  'settings.image_backgrounds_users': '접힌 글에 이미지 배경 주기',
-  'settings.inline_preview_cards': '외부 링크에 대한 미리보기 카드를 같이 표시',
-  'settings.layout': '레이아웃:',
-  'settings.layout_opts': '레이아웃 옵션',
-  'settings.media': '미디어',
-  'settings.media_fullwidth': '최대폭 미디어 미리보기',
-  'settings.media_letterbox': '레터박스 미디어',
-  'settings.media_letterbox_hint': '확대하고 자르는 대신 축소하고 레터박스에 넣어 이미지를 보여줍니다',
-  'settings.media_reveal_behind_cw': '열람주의로 가려진 미디어를 기본으로 펼쳐 둡니다',
-  'settings.navbar_under': '내비바를 하단에 (모바일 전용)',
-  'settings.notifications.favicon_badge': '읽지 않은 알림 파비콘 배지',
-  'settings.notifications.favicon_badge.hint': '읽지 않은 알림 배지를 파비콘에 추가합니다',
-  'settings.notifications.tab_badge': '읽지 않은 알림 배지',
-  'settings.notifications.tab_badge.hint': '알림 컬럼이 열려 있지 않을 때 알림 컬럼에 알림이 있다는 배지를 표시합니다',
-  'settings.notifications_opts': '알림 옵션',
-  'settings.pop_in_left': '왼쪽',
-  'settings.pop_in_player': '떠있는 재생기 활성화',
-  'settings.pop_in_position': '떠있는 재생기 위치:',
-  'settings.pop_in_right': '오른쪽',
-  'settings.preferences': '사용자 설정',
-  'settings.prepend_cw_re': '열람주의가 달린 글에 답장을 할 때 열람주의 문구 앞에 “re: ”를 추가합니다',
-  'settings.preselect_on_reply': '답글 달 때 사용자명 미리 선택',
-  'settings.preselect_on_reply_hint': '답글을 달 때 이미 멘션 된 사람의 사용자명을 미리 블럭으로 설정해 놓습니다',
-  'settings.rewrite_mentions': '표시되는 게시물의 멘션 표시 바꾸기',
-  'settings.rewrite_mentions_acct': '사용자명과 도메인으로 바꾸기(계정이 원격일 때)',
-  'settings.rewrite_mentions_no': '멘션을 그대로 두기',
-  'settings.rewrite_mentions_username': '사용자명으로 바꾸기',
-  'settings.shared_settings_link': '사용자 설정',
-  'settings.show_action_bar': '접힌 글에 액션 버튼들 보이기',
-  'settings.show_content_type_choice': '글을 작성할 때 콘텐트 타입을 고를 수 있도록 합니다',
-  'settings.show_reply_counter': '대략적인 답글 개수를 표시합니다',
-  'settings.side_arm': '보조 작성 버튼:',
-  'settings.side_arm.none': '없음',
-  'settings.side_arm_reply_mode': '답글을 작성할 때:',
-  'settings.side_arm_reply_mode.copy': '답글을 달려는 글의 공개설정을 복사합니다',
-  'settings.side_arm_reply_mode.keep': '보조 작성 버튼의 공개설정을 유지합니다',
-  'settings.side_arm_reply_mode.restrict': '답글을 달려는 글의 공개설정에 맞게 제한합니다',
-  'settings.status_icons': '게시물 아이콘',
-  'settings.status_icons_language': '언어 표시',
-  'settings.status_icons_local_only': '로컬 전용 표시',
-  'settings.status_icons_media': '미디어와 투표 표시',
-  'settings.status_icons_reply': '답글 표시',
-  'settings.status_icons_visibility': '툿 공개설정 표시',
-  'settings.swipe_to_change_columns': '스와이프하여 컬럼간 전환을 허용합니다 (모바일 전용)',
-  'settings.tag_misleading_links': '오해의 소지가 있는 링크를 표시합니다',
-  'settings.tag_misleading_links.hint': '링크에 명시적으로 주소가 없는 경우엔 대상 호스트를 보이도록 표시합니다',
-  'settings.wide_view': '넓은 뷰 (데스크탑 모드 전용)',
-  'settings.wide_view_hint': '컬럼들을 늘려서 활용 가능한 공간을 사용합니다.',
-  'status.collapse': '접기',
-  'status.has_audio': '소리 파일이 첨부되어 있습니다',
-  'status.has_pictures': '그림 파일이 첨부되어 있습니다',
-  'status.has_preview_card': '미리보기 카드가 첨부되어 있습니다',
-  'status.has_video': '영상이 첨부되어 있습니다',
-  'status.hide': '글 가리기',
-  'status.in_reply_to': '이 글은 답글입니다',
-  'status.is_poll': '이 글은 설문입니다',
-  'status.local_only': '당신의 서버에서만 보입니다',
-  'status.sensitive_toggle': '클릭해서 보기',
-  'status.show_filter_reason': '(이유 보기)',
-  'status.uncollapse': '펼치기',
-  'upload_modal.applying': '적용중…',
-  'web_app_crash.change_your_settings': '{settings}을 바꾸세요',
-  'web_app_crash.content': '이것들을 시도해 볼 수 있습니다:',
-  'web_app_crash.debug_info': '디버그 정보',
-  'web_app_crash.disable_addons': '브라우저 애드온이나 기본 번역 도구를 비활성화 합니다',
-  'web_app_crash.issue_tracker': '이슈 트래커',
-  'web_app_crash.reload': '새로고침',
-  'web_app_crash.reload_page': '이 페이지를 {reload}',
-  'web_app_crash.report_issue': '{issuetracker}에 버그 제보',
-  'web_app_crash.settings': '설정',
-  'web_app_crash.title': '죄송합니다, 하지만 마스토돈 앱이 뭔가 잘못되었습니다.',
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ko.json b/app/javascript/flavours/glitch/locales/ko.json
new file mode 100644
index 000000000..f17f32a9d
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ko.json
@@ -0,0 +1,200 @@
+{
+  "about.fork_disclaimer": "글리치는 마스토돈에서 포크한 자유 오픈소스 소프트웨어입니다.",
+  "account.add_account_note": "@{name} 님에 대한 메모 추가",
+  "account.disclaimer_full": "아래에 있는 정보들은 사용자의 프로필을 완벽하게 나타내지 못하고 있을 수도 있습니다.",
+  "account.follows": "팔로우",
+  "account.joined": "{date}에 가입함",
+  "account.suspended_disclaimer_full": "이 사용자는 중재자에 의해 정지되었습니다.",
+  "account.view_full_profile": "전체 프로필 보기",
+  "account_note.cancel": "취소",
+  "account_note.edit": "편집",
+  "account_note.glitch_placeholder": "코멘트가 없습니다",
+  "account_note.save": "저장",
+  "advanced_options.icon_title": "고급 옵션",
+  "advanced_options.local-only.long": "다른 서버에 게시하지 않기",
+  "advanced_options.local-only.short": "로컬 전용",
+  "advanced_options.local-only.tooltip": "이 게시물은 로컬 전용입니다",
+  "advanced_options.threaded_mode.long": "글을 작성하고 자동으로 답글 열기",
+  "advanced_options.threaded_mode.short": "글타래 모드",
+  "advanced_options.threaded_mode.tooltip": "글타래 모드 활성화됨",
+  "boost_modal.missing_description": "이 게시물은 설명이 없는 미디어를 포함하고 있습니다",
+  "column.favourited_by": "즐겨찾기 한 사람",
+  "column.heading": "기타",
+  "column.reblogged_by": "부스트 한 사람",
+  "column.subheading": "다양한 옵션",
+  "column_header.profile": "프로필",
+  "column_subheading.lists": "리스트",
+  "column_subheading.navigation": "탐색",
+  "community.column_settings.allow_local_only": "로컬 전용 글 보기",
+  "compose.attach": "첨부…",
+  "compose.attach.doodle": "뭔가 그려보세요",
+  "compose.attach.upload": "파일 업로드",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "마크다운",
+  "compose.content-type.plain": "일반 텍스트",
+  "compose_form.poll.multiple_choices": "여러 개 선택 가능",
+  "compose_form.poll.single_choice": "하나만 선택 가능",
+  "compose_form.spoiler": "경고 메시지로 숨기기",
+  "confirmation_modal.do_not_ask_again": "다음부터 확인창을 띄우지 않기",
+  "confirmations.deprecated_settings.confirm": "마스토돈 설정 사용",
+  "confirmations.deprecated_settings.message": "사용하던 몇몇 기기별 글리치 {app_settings}은 마스토돈 {preferences}으로 대체되었습니다:",
+  "confirmations.missing_media_description.confirm": "그냥 보내기",
+  "confirmations.missing_media_description.edit": "미디어 편집",
+  "confirmations.missing_media_description.message": "하나 이상의 미디어에 대해 설명을 작성하지 않았습니다. 시각장애인을 위해 모든 미디어에 설명을 추가하는 것을 고려해주세요.",
+  "confirmations.unfilter.author": "작성자",
+  "confirmations.unfilter.confirm": "보기",
+  "confirmations.unfilter.edit_filter": "필터 편집",
+  "confirmations.unfilter.filters": "적용된 {count, plural, one {필터} other {필터들}}",
+  "content-type.change": "콘텐트 타입",
+  "direct.group_by_conversations": "대화별로 묶기",
+  "endorsed_accounts_editor.endorsed_accounts": "추천하는 계정들",
+  "favourite_modal.combo": "다음엔 {combo}를 눌러 건너뛸 수 있습니다",
+  "getting_started.onboarding": "둘러보기",
+  "home.column_settings.advanced": "고급",
+  "home.column_settings.filter_regex": "정규표현식으로 필터",
+  "home.column_settings.show_direct": "DM 보여주기",
+  "home.settings": "컬럼 설정",
+  "keyboard_shortcuts.bookmark": "북마크",
+  "keyboard_shortcuts.secondary_toot": "보조 프라이버시 설정으로 글 보내기",
+  "keyboard_shortcuts.toggle_collapse": "글 접거나 펼치기",
+  "layout.auto": "자동",
+  "layout.desktop": "데스크탑",
+  "layout.hint.auto": "“고급 웹 인터페이스 활성화” 설정과 화면 크기에 따라 자동으로 레이아웃을 고릅니다.",
+  "layout.hint.desktop": "“고급 웹 인터페이스 활성화” 설정이나 화면 크기에 관계 없이 멀티 컬럼 레이아웃을 사용합니다.",
+  "layout.hint.single": "“고급 웹 인터페이스 활성화” 설정이나 화면 크기에 관계 없이 싱글 컬럼 레이아웃을 사용합니다.",
+  "layout.single": "모바일",
+  "media_gallery.sensitive": "민감함",
+  "moved_to_warning": "이 계정은 {moved_to_link}로 이동한 것으로 표시되었고, 새 팔로우를 받지 않는 것 같습니다.",
+  "navigation_bar.app_settings": "앱 설정",
+  "navigation_bar.featured_users": "추천된 계정들",
+  "navigation_bar.info": "추가 정보",
+  "navigation_bar.keyboard_shortcuts": "키보드 단축기",
+  "navigation_bar.misc": "다양한 옵션들",
+  "notification.markForDeletion": "삭제하기 위해 표시",
+  "notification_purge.btn_all": "전체선택",
+  "notification_purge.btn_apply": "선택된 알림 삭제",
+  "notification_purge.btn_invert": "선택반전",
+  "notification_purge.btn_none": "전체선택해제",
+  "notification_purge.start": "알림 삭제모드로 들어가기",
+  "notifications.marked_clear": "선택된 알림 모두 삭제",
+  "notifications.marked_clear_confirmation": "정말로 선택된 알림들을 영구적으로 삭제할까요?",
+  "onboarding.done": "완료",
+  "onboarding.next": "다음",
+  "onboarding.page_five.public_timelines": "로컬 타임라인은 {domain}에 있는 모든 사람의 공개글을 보여줍니다. 연합 타임라인은 {domain}에 있는 사람들이 팔로우 하는 모든 사람의 공개글을 보여줍니다. 이것들은 공개 타임라인이라고 불리며, 새로운 사람들을 발견할 수 있는 좋은 방법입니다.",
+  "onboarding.page_four.home": "홈 타임라인은 당신이 팔로우 한 사람들의 글을 보여줍니다.",
+  "onboarding.page_four.notifications": "알림 컬럼은 누군가가 당신과 상호작용한 것들을 보여줍니다.",
+  "onboarding.page_one.federation": "{domain}은 마스토돈의 '인스턴스'입니다. 마스토돈은 하나의 거대한 소셜 네트워크를 만들기 위해 참여한 서버들의 네트워크입니다. 우린 이 서버들을 인스턴스라고 부릅니다.",
+  "onboarding.page_one.handle": "당신은 {domain}에 속해 있으며, 전체 핸들은 {handle} 입니다.",
+  "onboarding.page_one.welcome": "{domain}에 오신 것을 환영합니다!",
+  "onboarding.page_six.admin": "우리 서버의 관리자는 {admin} 님입니다.",
+  "onboarding.page_six.almost_done": "거의 다 되었습니다…",
+  "onboarding.page_six.appetoot": "본 아페툿!",
+  "onboarding.page_six.apps_available": "iOS, 안드로이드, 그리고 다른 플랫폼들을 위한 {apps}이 존재합니다.",
+  "onboarding.page_six.github": "{domain}은 글리치를 통해 구동 됩니다. 글리치는 {Mastodon}의 {fork}입니다, 그리고 어떤 마스토돈 인스턴스나 앱과도 호환 됩니다. 글리치는 완전한 자유 오픈소스입니다. {github}에서 버그를 리포팅 하거나, 기능을 제안하거나, 코드를 기여할 수 있습니다.",
+  "onboarding.page_six.guidelines": "커뮤니티 가이드라인",
+  "onboarding.page_six.read_guidelines": "{domain}의 {guidelines}을 읽어주세요!",
+  "onboarding.page_six.various_app": "모바일 앱",
+  "onboarding.page_three.profile": "프로필을 수정해 아바타, 바이오, 표시되는 이름을 설정하세요. 거기에서 다른 설정들도 찾을 수 있습니다.",
+  "onboarding.page_three.search": "검색창을 사용해 사람들과 해시태그를 찾아보세요. 예를 들면 {illustration}이라든지 {introcustions} 같은 것으로요. 이 인스턴스에 있지 않은 사람을 찾으려면, 전체 핸들을 사용하세요.",
+  "onboarding.page_two.compose": "작성 컬럼에서 게시물을 작성하세요. 그림을 업로드 할 수 있고, 공개설정을 바꿀 수도 있으며, 아래 아이콘을 통해 열람주의 텍스트를 설정할 수 있습니다.",
+  "onboarding.skip": "건너뛰기",
+  "settings.always_show_spoilers_field": "열람주의 항목을 언제나 활성화",
+  "settings.auto_collapse": "자동으로 접기",
+  "settings.auto_collapse_all": "모두",
+  "settings.auto_collapse_lengthy": "긴 글",
+  "settings.auto_collapse_media": "미디어 포함 글",
+  "settings.auto_collapse_notifications": "알림",
+  "settings.auto_collapse_reblogs": "부스트",
+  "settings.auto_collapse_replies": "답글",
+  "settings.close": "닫기",
+  "settings.collapsed_statuses": "접힌 글",
+  "settings.compose_box_opts": "작성 상자",
+  "settings.confirm_before_clearing_draft": "작성 중인 메시지를 덮어씌우기 전에 확인창을 보여주기",
+  "settings.confirm_boost_missing_media_description": "미디어 설명이 없는 글을 부스트하려 할 때 확인창을 보여주기",
+  "settings.confirm_missing_media_description": "미디어 설명이 없는 글을 작성하려 할 때 확인창을 보여주기",
+  "settings.content_warnings": "열람주의",
+  "settings.content_warnings.regexp": "정규표현식",
+  "settings.content_warnings_filter": "자동으로 펼치지 않을 열람주의 문구:",
+  "settings.content_warnings_media_outside": "미디어 첨부를 열람주의 바깥에 보이기",
+  "settings.content_warnings_media_outside_hint": "마스토돈 원본처럼 열람주의 토글이 미디어 첨부에는 영향을 미치지 않게 합니다",
+  "settings.content_warnings_shared_state": "동일한 글의 열람주의를 한번에 열고 닫기",
+  "settings.content_warnings_shared_state_hint": "마스토돈 원본처럼 열람주의 버튼이 동일한 모든 글에 대해 영향을 미치게 합니다. 펼쳐진 열람주의 글이 자동으로 다시 접히는 것을 방지합니다",
+  "settings.content_warnings_unfold_opts": "자동 펼치기 옵션",
+  "settings.deprecated_setting": "이 설정은 마스토돈의 {settings_page_link}에서 관리됩니다",
+  "settings.enable_collapsed": "접힌 글 활성화",
+  "settings.enable_collapsed_hint": "접힌 게시물을 콘텐츠의 일부분을 가려서 공간을 적게 차지합니다. 열람주의 기능과는 다릅니다",
+  "settings.enable_content_warnings_auto_unfold": "자동으로 열람주의 펼치기",
+  "settings.general": "일반",
+  "settings.hicolor_privacy_icons": "높은 채도의 공개설정 아이콘",
+  "settings.hicolor_privacy_icons.hint": "공개설정 아이콘들을 밝고 구분하기 쉬운 색으로 표시합니다",
+  "settings.image_backgrounds": "이미지 배경",
+  "settings.image_backgrounds_media": "접힌 글의 미디어 미리보기",
+  "settings.image_backgrounds_media_hint": "게시물이 미디어 첨부를 포함한다면, 첫번째를 배경으로 사용합니다",
+  "settings.image_backgrounds_users": "접힌 글에 이미지 배경 주기",
+  "settings.inline_preview_cards": "외부 링크에 대한 미리보기 카드를 같이 표시",
+  "settings.layout": "레이아웃:",
+  "settings.layout_opts": "레이아웃 옵션",
+  "settings.media": "미디어",
+  "settings.media_fullwidth": "최대폭 미디어 미리보기",
+  "settings.media_letterbox": "레터박스 미디어",
+  "settings.media_letterbox_hint": "확대하고 자르는 대신 축소하고 레터박스에 넣어 이미지를 보여줍니다",
+  "settings.media_reveal_behind_cw": "열람주의로 가려진 미디어를 기본으로 펼쳐 둡니다",
+  "settings.notifications.favicon_badge": "읽지 않은 알림 파비콘 배지",
+  "settings.notifications.favicon_badge.hint": "읽지 않은 알림 배지를 파비콘에 추가합니다",
+  "settings.notifications.tab_badge": "읽지 않은 알림 배지",
+  "settings.notifications.tab_badge.hint": "알림 컬럼이 열려 있지 않을 때 알림 컬럼에 알림이 있다는 배지를 표시합니다",
+  "settings.notifications_opts": "알림 옵션",
+  "settings.pop_in_left": "왼쪽",
+  "settings.pop_in_player": "떠있는 재생기 활성화",
+  "settings.pop_in_position": "떠있는 재생기 위치:",
+  "settings.pop_in_right": "오른쪽",
+  "settings.preferences": "사용자 설정",
+  "settings.prepend_cw_re": "열람주의가 달린 글에 답장을 할 때 열람주의 문구 앞에 “re: ”를 추가합니다",
+  "settings.preselect_on_reply": "답글 달 때 사용자명 미리 선택",
+  "settings.preselect_on_reply_hint": "답글을 달 때 이미 멘션 된 사람의 사용자명을 미리 블럭으로 설정해 놓습니다",
+  "settings.rewrite_mentions": "표시되는 게시물의 멘션 표시 바꾸기",
+  "settings.rewrite_mentions_acct": "사용자명과 도메인으로 바꾸기(계정이 원격일 때)",
+  "settings.rewrite_mentions_no": "멘션을 그대로 두기",
+  "settings.rewrite_mentions_username": "사용자명으로 바꾸기",
+  "settings.shared_settings_link": "사용자 설정",
+  "settings.show_action_bar": "접힌 글에 액션 버튼들 보이기",
+  "settings.show_content_type_choice": "글을 작성할 때 콘텐트 타입을 고를 수 있도록 합니다",
+  "settings.show_reply_counter": "대략적인 답글 개수를 표시합니다",
+  "settings.side_arm": "보조 작성 버튼:",
+  "settings.side_arm.none": "없음",
+  "settings.side_arm_reply_mode": "답글을 작성할 때:",
+  "settings.side_arm_reply_mode.copy": "답글을 달려는 글의 공개설정을 복사합니다",
+  "settings.side_arm_reply_mode.keep": "보조 작성 버튼의 공개설정을 유지합니다",
+  "settings.side_arm_reply_mode.restrict": "답글을 달려는 글의 공개설정에 맞게 제한합니다",
+  "settings.status_icons": "게시물 아이콘",
+  "settings.status_icons_language": "언어 표시",
+  "settings.status_icons_local_only": "로컬 전용 표시",
+  "settings.status_icons_media": "미디어와 투표 표시",
+  "settings.status_icons_reply": "답글 표시",
+  "settings.status_icons_visibility": "툿 공개설정 표시",
+  "settings.swipe_to_change_columns": "스와이프하여 컬럼간 전환을 허용합니다 (모바일 전용)",
+  "settings.tag_misleading_links": "오해의 소지가 있는 링크를 표시합니다",
+  "settings.tag_misleading_links.hint": "링크에 명시적으로 주소가 없는 경우엔 대상 호스트를 보이도록 표시합니다",
+  "settings.wide_view": "넓은 뷰 (데스크탑 모드 전용)",
+  "settings.wide_view_hint": "컬럼들을 늘려서 활용 가능한 공간을 사용합니다.",
+  "status.collapse": "접기",
+  "status.has_audio": "소리 파일이 첨부되어 있습니다",
+  "status.has_pictures": "그림 파일이 첨부되어 있습니다",
+  "status.has_preview_card": "미리보기 카드가 첨부되어 있습니다",
+  "status.has_video": "영상이 첨부되어 있습니다",
+  "status.in_reply_to": "이 글은 답글입니다",
+  "status.is_poll": "이 글은 설문입니다",
+  "status.local_only": "당신의 서버에서만 보입니다",
+  "status.sensitive_toggle": "클릭해서 보기",
+  "status.uncollapse": "펼치기",
+  "web_app_crash.change_your_settings": "{settings}을 바꾸세요",
+  "web_app_crash.content": "이것들을 시도해 볼 수 있습니다:",
+  "web_app_crash.debug_info": "디버그 정보",
+  "web_app_crash.disable_addons": "브라우저 애드온이나 기본 번역 도구를 비활성화 합니다",
+  "web_app_crash.issue_tracker": "이슈 트래커",
+  "web_app_crash.reload": "새로고침",
+  "web_app_crash.reload_page": "이 페이지를 {reload}",
+  "web_app_crash.report_issue": "{issuetracker}에 버그 제보",
+  "web_app_crash.settings": "설정",
+  "web_app_crash.title": "죄송합니다, 하지만 마스토돈 앱이 뭔가 잘못되었습니다."
+}
diff --git a/app/javascript/flavours/glitch/locales/ku.js b/app/javascript/flavours/glitch/locales/ku.js
deleted file mode 100644
index 19e0e95aa..000000000
--- a/app/javascript/flavours/glitch/locales/ku.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ku.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ku.json b/app/javascript/flavours/glitch/locales/ku.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ku.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/kw.js b/app/javascript/flavours/glitch/locales/kw.js
deleted file mode 100644
index 1325ca825..000000000
--- a/app/javascript/flavours/glitch/locales/kw.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/kw.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/kw.json b/app/javascript/flavours/glitch/locales/kw.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/kw.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/la.json b/app/javascript/flavours/glitch/locales/la.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/la.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/lt.js b/app/javascript/flavours/glitch/locales/lt.js
deleted file mode 100644
index 47453aeeb..000000000
--- a/app/javascript/flavours/glitch/locales/lt.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/lt.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/lt.json b/app/javascript/flavours/glitch/locales/lt.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/lt.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/lv.js b/app/javascript/flavours/glitch/locales/lv.js
deleted file mode 100644
index cdbcdf799..000000000
--- a/app/javascript/flavours/glitch/locales/lv.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/lv.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/lv.json b/app/javascript/flavours/glitch/locales/lv.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/lv.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/mk.js b/app/javascript/flavours/glitch/locales/mk.js
deleted file mode 100644
index 55e510b59..000000000
--- a/app/javascript/flavours/glitch/locales/mk.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/mk.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/mk.json b/app/javascript/flavours/glitch/locales/mk.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/mk.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ml.js b/app/javascript/flavours/glitch/locales/ml.js
deleted file mode 100644
index d00331a1a..000000000
--- a/app/javascript/flavours/glitch/locales/ml.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ml.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ml.json b/app/javascript/flavours/glitch/locales/ml.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ml.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/mr.js b/app/javascript/flavours/glitch/locales/mr.js
deleted file mode 100644
index fb3cde92a..000000000
--- a/app/javascript/flavours/glitch/locales/mr.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/mr.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/mr.json b/app/javascript/flavours/glitch/locales/mr.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/mr.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ms.js b/app/javascript/flavours/glitch/locales/ms.js
deleted file mode 100644
index 61033c521..000000000
--- a/app/javascript/flavours/glitch/locales/ms.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ms.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ms.json b/app/javascript/flavours/glitch/locales/ms.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ms.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/my.json b/app/javascript/flavours/glitch/locales/my.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/my.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/nl.js b/app/javascript/flavours/glitch/locales/nl.js
deleted file mode 100644
index 17c371c58..000000000
--- a/app/javascript/flavours/glitch/locales/nl.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/nl.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/nl.json b/app/javascript/flavours/glitch/locales/nl.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/nl.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/nn.js b/app/javascript/flavours/glitch/locales/nn.js
deleted file mode 100644
index 4c42368cb..000000000
--- a/app/javascript/flavours/glitch/locales/nn.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/nn.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/nn.json b/app/javascript/flavours/glitch/locales/nn.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/nn.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/no.js b/app/javascript/flavours/glitch/locales/no.js
deleted file mode 100644
index 794b1da25..000000000
--- a/app/javascript/flavours/glitch/locales/no.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/no.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/no.json b/app/javascript/flavours/glitch/locales/no.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/no.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/oc.js b/app/javascript/flavours/glitch/locales/oc.js
deleted file mode 100644
index 8f161fd8c..000000000
--- a/app/javascript/flavours/glitch/locales/oc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/oc.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/oc.json b/app/javascript/flavours/glitch/locales/oc.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/oc.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/pa.js b/app/javascript/flavours/glitch/locales/pa.js
deleted file mode 100644
index c3e0e2b84..000000000
--- a/app/javascript/flavours/glitch/locales/pa.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/pa.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/pa.json b/app/javascript/flavours/glitch/locales/pa.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/pa.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/pl.js b/app/javascript/flavours/glitch/locales/pl.js
deleted file mode 100644
index f430bf577..000000000
--- a/app/javascript/flavours/glitch/locales/pl.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import inherited from 'mastodon/locales/pl.json';
-
-const messages = {
-  'getting_started.open_source_notice': 'Glitchsoc jest wolnym i otwartoźródłowym forkiem oprogramowania {Mastodon}. Możesz współtworzyć projekt lub zgłaszać błędy na GitHubie pod adresem {github}.',
-  'layout.auto': 'Automatyczny',
-  'layout.current_is': 'Twój obecny układ to:',
-  'layout.desktop': 'Desktopowy',
-  'layout.mobile': 'Mobilny',
-  'navigation_bar.app_settings': 'Ustawienia aplikacji',
-  'navigation_bar.bookmarks': 'Zakładki',
-  'getting_started.onboarding': 'Rozejrzyj się',
-  'onboarding.page_one.federation': '{domain} jest \'instancją\' Mastodona. Mastodon to sieć działających niezależnie serwerów tworzących jedną sieć społecznościową. Te serwery nazywane są instancjami.',
-  'onboarding.page_one.welcome': 'Witamy na {domain}!',
-  'onboarding.page_six.github': '{domain} jest oparty na Glitchsoc. Glitchsoc jest {forkiem} {Mastodon}a kompatybilnym z każdym klientem i aplikacją Mastodona. Glitchsoc jest całkowicie wolnym i otwartoźródłowym oprogramowaniem. Możesz zgłaszać błędy i sugestie funkcji oraz współtworzyć projekt na {github}.',
-  'settings.auto_collapse': 'Automatyczne zwijanie',
-  'settings.auto_collapse_all': 'Wszystko',
-  'settings.auto_collapse_lengthy': 'Długie wpisy',
-  'settings.auto_collapse_media': 'Wpisy z zawartością multimedialną',
-  'settings.auto_collapse_notifications': 'Powiadomienia',
-  'settings.auto_collapse_reblogs': 'Podbicia',
-  'settings.auto_collapse_replies': 'Odpowiedzi',
-  'settings.close': 'Zamknij',
-  'settings.collapsed_statuses': 'Zwijanie wpisów',
-  'settings.enable_collapsed': 'Włącz zwijanie wpisów',
-  'settings.general': 'Ogólne',
-  'settings.image_backgrounds': 'Obrazy w tle',
-  'settings.image_backgrounds_media': 'Wyświetlaj zawartość multimedialną zwiniętych wpisów',
-  'settings.image_backgrounds_users': 'Nadaj tło zwiniętym wpisom',
-  'settings.layout': 'Układ',
-  'settings.media': 'Zawartość multimedialna',
-  'settings.media_letterbox': 'Letterbox media',
-  'settings.media_fullwidth': 'Podgląd zawartości multimedialnej o pełnej szerokości',
-  'settings.navbar_under': 'Pasek nawigacji na dole (tylko w trybie mobilnym)',
-  'settings.preferences': 'Preferencje użytkownika',
-  'settings.side_arm': 'Drugi przycisk wysyłania',
-  'settings.side_arm.none': 'Żaden',
-  'settings.wide_view': 'Szeroki widok (tylko w trybie desktopowym)',
-  'status.bookmark': 'Dodaj do zakładek',
-  'status.collapse': 'Zwiń',
-  'status.uncollapse': 'Rozwiń',
-
-  'media_gallery.sensitive': 'Zawartość wrażliwa',
-
-  'favourite_modal.combo': 'Możesz nacisnąć {combo}, aby pominąć to następnym razem',
-
-  'home.column_settings.show_direct': 'Pokaż wiadomości bezpośrednie',
-
-  'notification.markForDeletion': 'Oznacz do usunięcia',
-  'notifications.clear': 'Wyczyść wszystkie powiadomienia',
-  'notifications.marked_clear_confirmation': 'Czy na pewno chcesz bezpowrtonie usunąć wszystkie powiadomienia?',
-  'notifications.marked_clear': 'Usuń zaznaczone powiadomienia',
-
-  'notification_purge.btn_all': 'Zaznacz\nwszystkie',
-  'notification_purge.btn_none': 'Odznacz\nwszystkie',
-  'notification_purge.btn_invert': 'Odwróć\nzaznaczenie',
-  'notification_purge.btn_apply': 'Usuń\nzaznaczone',
-  'notification_purge.start': 'Przejdź do trybu usuwania powiadomień',
-
-  'compose.attach.upload': 'Wyślij plik',
-  'compose.attach.doodle': 'Narysuj coś',
-  'compose.attach': 'Załącz coś',
-
-  'advanced_options.local-only.short': 'Tylko lokalnie',
-  'advanced_options.local-only.long': 'Nie wysyłaj na inne instancje',
-  'advanced_options.local-only.tooltip': 'Ten wpis jest widoczny tylko lokalnie',
-  'advanced_options.icon_title': 'Ustawienia zaawansowane',
-  'advanced_options.threaded_mode.short': 'Tryb wątków',
-  'advanced_options.threaded_mode.long': 'Przechodzi do tworzenia odpowiedzi po publikacji wpisu',
-  'advanced_options.threaded_mode.tooltip': 'Włączono tryb wątków',
-  
-  'column.bookmarks': 'Zakładki',
-  'compose_form.sensitive': 'Oznacz zawartość multimedialną jako wrażliwą',
-  'compose_form.spoiler': 'Ukryj tekst za ostrzeżeniem',
-  'favourite_modal.combo': 'Możesz nacisnąć {combo}, aby pominąć to następnym razem',
-  'tabs_bar.compose': 'Napisz',
-  
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/pl.json b/app/javascript/flavours/glitch/locales/pl.json
new file mode 100644
index 000000000..09acd098e
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/pl.json
@@ -0,0 +1,55 @@
+{
+  "advanced_options.icon_title": "Ustawienia zaawansowane",
+  "advanced_options.local-only.long": "Nie wysyłaj na inne instancje",
+  "advanced_options.local-only.short": "Tylko lokalnie",
+  "advanced_options.local-only.tooltip": "Ten wpis jest widoczny tylko lokalnie",
+  "advanced_options.threaded_mode.long": "Przechodzi do tworzenia odpowiedzi po publikacji wpisu",
+  "advanced_options.threaded_mode.short": "Tryb wątków",
+  "advanced_options.threaded_mode.tooltip": "Włączono tryb wątków",
+  "compose.attach": "Załącz coś",
+  "compose.attach.doodle": "Narysuj coś",
+  "compose.attach.upload": "Wyślij plik",
+  "compose_form.spoiler": "Ukryj tekst za ostrzeżeniem",
+  "favourite_modal.combo": "Możesz nacisnąć {combo}, aby pominąć to następnym razem",
+  "getting_started.onboarding": "Rozejrzyj się",
+  "home.column_settings.show_direct": "Pokaż wiadomości bezpośrednie",
+  "layout.auto": "Automatyczny",
+  "layout.desktop": "Desktopowy",
+  "media_gallery.sensitive": "Zawartość wrażliwa",
+  "navigation_bar.app_settings": "Ustawienia aplikacji",
+  "notification.markForDeletion": "Oznacz do usunięcia",
+  "notification_purge.btn_all": "Zaznacz\nwszystkie",
+  "notification_purge.btn_apply": "Usuń\nzaznaczone",
+  "notification_purge.btn_invert": "Odwróć\nzaznaczenie",
+  "notification_purge.btn_none": "Odznacz\nwszystkie",
+  "notification_purge.start": "Przejdź do trybu usuwania powiadomień",
+  "notifications.marked_clear": "Usuń zaznaczone powiadomienia",
+  "notifications.marked_clear_confirmation": "Czy na pewno chcesz bezpowrtonie usunąć wszystkie powiadomienia?",
+  "onboarding.page_one.federation": "{domain} jest 'instancją' Mastodona. Mastodon to sieć działających niezależnie serwerów tworzących jedną sieć społecznościową. Te serwery nazywane są instancjami.",
+  "onboarding.page_one.welcome": "Witamy na {domain}!",
+  "onboarding.page_six.github": "{domain} jest oparty na Glitchsoc. Glitchsoc jest {forkiem} {Mastodon}a kompatybilnym z każdym klientem i aplikacją Mastodona. Glitchsoc jest całkowicie wolnym i otwartoźródłowym oprogramowaniem. Możesz zgłaszać błędy i sugestie funkcji oraz współtworzyć projekt na {github}.",
+  "settings.auto_collapse": "Automatyczne zwijanie",
+  "settings.auto_collapse_all": "Wszystko",
+  "settings.auto_collapse_lengthy": "Długie wpisy",
+  "settings.auto_collapse_media": "Wpisy z zawartością multimedialną",
+  "settings.auto_collapse_notifications": "Powiadomienia",
+  "settings.auto_collapse_reblogs": "Podbicia",
+  "settings.auto_collapse_replies": "Odpowiedzi",
+  "settings.close": "Zamknij",
+  "settings.collapsed_statuses": "Zwijanie wpisów",
+  "settings.content_warnings": "Content warnings",
+  "settings.enable_collapsed": "Włącz zwijanie wpisów",
+  "settings.general": "Ogólne",
+  "settings.image_backgrounds": "Obrazy w tle",
+  "settings.image_backgrounds_media": "Wyświetlaj zawartość multimedialną zwiniętych wpisów",
+  "settings.image_backgrounds_users": "Nadaj tło zwiniętym wpisom",
+  "settings.layout": "Układ",
+  "settings.media": "Zawartość multimedialna",
+  "settings.media_fullwidth": "Podgląd zawartości multimedialnej o pełnej szerokości",
+  "settings.preferences": "Preferencje użytkownika",
+  "settings.side_arm": "Drugi przycisk wysyłania",
+  "settings.side_arm.none": "Żaden",
+  "settings.wide_view": "Szeroki widok (tylko w trybie desktopowym)",
+  "status.collapse": "Zwiń",
+  "status.uncollapse": "Rozwiń"
+}
diff --git a/app/javascript/flavours/glitch/locales/pt-BR.js b/app/javascript/flavours/glitch/locales/pt-BR.js
deleted file mode 100644
index 6fed635f8..000000000
--- a/app/javascript/flavours/glitch/locales/pt-BR.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/pt-BR.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/pt-BR.json b/app/javascript/flavours/glitch/locales/pt-BR.json
new file mode 100644
index 000000000..0bc0d2bea
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/pt-BR.json
@@ -0,0 +1,200 @@
+{
+  "about.fork_disclaimer": "O Glitch-soc é um software gratuito de código aberto bifurcado a partir do Mastodon.",
+  "account.add_account_note": "Adicionar nota para @{name}",
+  "account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de forma incompleta.",
+  "account.follows": "Seguidores",
+  "account.joined": "Entrou em {date}",
+  "account.suspended_disclaimer_full": "Este usuário foi suspenso por um moderador.",
+  "account.view_full_profile": "Ver o perfil completo",
+  "account_note.cancel": "Cancelar",
+  "account_note.edit": "Editar",
+  "account_note.glitch_placeholder": "Nenhum comentário fornecido",
+  "account_note.save": "Salvar",
+  "advanced_options.icon_title": "Opções avançadas",
+  "advanced_options.local-only.long": "Não publicar em outras instâncias",
+  "advanced_options.local-only.short": "Apenas localmente",
+  "advanced_options.local-only.tooltip": "Este post é somente local",
+  "advanced_options.threaded_mode.long": "Abrir automaticamente uma resposta ao postar",
+  "advanced_options.threaded_mode.short": "Modo de discussão",
+  "advanced_options.threaded_mode.tooltip": "Modo de discussão ativado",
+  "boost_modal.missing_description": "Este toot contém algumas mídias sem descrição",
+  "column.favourited_by": "Favoritado por",
+  "column.heading": "Diversos",
+  "column.reblogged_by": "Inpulsionado por",
+  "column.subheading": "Opções diversas",
+  "column_header.profile": "Perfil",
+  "column_subheading.lists": "Listas",
+  "column_subheading.navigation": "Navegação",
+  "community.column_settings.allow_local_only": "Mostrar os toots apenas locais",
+  "compose.attach": "Anexar...",
+  "compose.attach.doodle": "Desenhe algo",
+  "compose.attach.upload": "Enviar um arquivo",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Texto sem formatação",
+  "compose_form.poll.multiple_choices": "Permitir múltipla escolha",
+  "compose_form.poll.single_choice": "Permitir uma escolha",
+  "compose_form.spoiler": "Ocultar texto atrás do aviso",
+  "confirmation_modal.do_not_ask_again": "Não pedir confirmação novamente",
+  "confirmations.deprecated_settings.confirm": "Usar preferências do Mastodon",
+  "confirmations.deprecated_settings.message": "Alguns dos {app_settings} específicos do dispositivo que você está usando foram substituídos por Mastodon {preferences} e serão substituídos:",
+  "confirmations.missing_media_description.confirm": "Enviar mesmo assim",
+  "confirmations.missing_media_description.edit": "Editar mídia",
+  "confirmations.missing_media_description.message": "Pelo menos um anexo de mídia não tem uma descrição. Considere descrever todos os anexos de mídia para deficientes visuais antes de enviar seu toot.",
+  "confirmations.unfilter.author": "Autor",
+  "confirmations.unfilter.confirm": "Exibir",
+  "confirmations.unfilter.edit_filter": "Editar filtro",
+  "confirmations.unfilter.filters": "Correspondência de {count, plural, one {filtro} other {filtros}}",
+  "content-type.change": "Tipo de conteúdo",
+  "direct.group_by_conversations": "Agrupar por conversa",
+  "endorsed_accounts_editor.endorsed_accounts": "Contas em destaque",
+  "favourite_modal.combo": "Você pode pressionar {combo} para pular isso da próxima vez",
+  "getting_started.onboarding": "Mostre-me ao redor",
+  "home.column_settings.advanced": "Avançado",
+  "home.column_settings.filter_regex": "Filtrar com uma expressão regular",
+  "home.column_settings.show_direct": "Mostrar DMs",
+  "home.settings": "Configurações da coluna",
+  "keyboard_shortcuts.bookmark": "para marcar",
+  "keyboard_shortcuts.secondary_toot": "para enviar toot usando a configuração de privacidade secundária",
+  "keyboard_shortcuts.toggle_collapse": "para recolher/mostrar toots",
+  "layout.auto": "Automático",
+  "layout.desktop": "Área de trabalho",
+  "layout.hint.auto": "Escolher automaticamente o layout baseado na configuração \"Habilitar interface web avançada\" e o tamanho da tela.",
+  "layout.hint.desktop": "Use o layout de várias colunas independentemente da configuração \"Habilitar interface web avançada\" ou do tamanho da tela.",
+  "layout.hint.single": "Use o layout de uma coluna independentemente da configuração \"Habilitar interface web avançada\" ou do tamanho da tela.",
+  "layout.single": "Celular",
+  "media_gallery.sensitive": "Sensível",
+  "moved_to_warning": "Esta conta foi como movida para {moved_to_link} e, portanto, pode não aceitar novos seguidores.",
+  "navigation_bar.app_settings": "Configurações do aplicativo",
+  "navigation_bar.featured_users": "Usuários em destaque",
+  "navigation_bar.info": "Informação estendida",
+  "navigation_bar.keyboard_shortcuts": "Atalhos de teclado",
+  "navigation_bar.misc": "Diversos",
+  "notification.markForDeletion": "Marcar para exclusão",
+  "notification_purge.btn_all": "Selecionar\ntudo",
+  "notification_purge.btn_apply": "Limpar\nselecionados",
+  "notification_purge.btn_invert": "Inverter\nseleção",
+  "notification_purge.btn_none": "Selecionar\nnenhum",
+  "notification_purge.start": "Entrar no modo de limpeza de notificação",
+  "notifications.marked_clear": "Limpar as notificações selecionadas",
+  "notifications.marked_clear_confirmation": "Tem certeza que deseja limpar todas as notificações selecionadas permanentemente?",
+  "onboarding.done": "Feito",
+  "onboarding.next": "Próximo",
+  "onboarding.page_five.public_timelines": "A linha do tempo local mostra publicações públicas de todos em {domain}. A linha do tempo federada mostra publicações públicas de todos que as pessoas seguem em {domain}. Estas são as linhas do tempo públicas, uma ótima maneira de descobrir novas pessoas.",
+  "onboarding.page_four.home": "A linha do tempo da casa mostra publicações de pessoas que você segue.",
+  "onboarding.page_four.notifications": "A coluna de notificações mostra quando alguém interage com você.",
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "Você está em {domain}, então o seu identificador completo é {handle}",
+  "onboarding.page_one.welcome": "Bem-vindo ao {domain}!",
+  "onboarding.page_six.admin": "O administrador da sua instância é {admin}.",
+  "onboarding.page_six.almost_done": "Quase pronto...",
+  "onboarding.page_six.appetoot": "Bom Appetoot!",
+  "onboarding.page_six.apps_available": "Há {apps} disponíveis para iOS, Android e outras plataformas.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "diretrizes da comunidade",
+  "onboarding.page_six.read_guidelines": "Por favor, leia {domain} {guidelines}!",
+  "onboarding.page_six.various_app": "aplicativos móveis",
+  "onboarding.page_three.profile": "Edite seu perfil para alterar seu avatar, bio e nome de exibição. Lá você também encontrará outras preferências.",
+  "onboarding.page_three.search": "Use a barra de busca para encontrar pessoas e procure hashtags, tais como {illustration} e {introductions}. Para procurar uma pessoa que não esteja neste caso, use o identificador completo.",
+  "onboarding.page_two.compose": "Escreva as postagens a partir da coluna de composição. Você pode enviar imagens, alterar as configurações de privacidade e adicionar avisos de conteúdo com os ícones abaixo.",
+  "onboarding.skip": "Pular",
+  "settings.always_show_spoilers_field": "Sempre ativar o campo Aviso de Conteúdo",
+  "settings.auto_collapse": "Colapso automático",
+  "settings.auto_collapse_all": "Tudo",
+  "settings.auto_collapse_lengthy": "Toots longos",
+  "settings.auto_collapse_media": "Toots com mídia",
+  "settings.auto_collapse_notifications": "Notificações",
+  "settings.auto_collapse_reblogs": "Impulsos",
+  "settings.auto_collapse_replies": "Respostas",
+  "settings.close": "Fechar",
+  "settings.collapsed_statuses": "Toots recolhidos",
+  "settings.compose_box_opts": "Caixa de composição",
+  "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmação antes de sobrescrever a mensagem que está sendo composta",
+  "settings.confirm_boost_missing_media_description": "Mostrar diálogo antes de inpulsionar os toots sem descrições de mídia",
+  "settings.confirm_missing_media_description": "Mostrar diálogo antes de enviar toots sem descrições de mídia",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Expressão regular",
+  "settings.content_warnings_filter": "Avisos de conteúdo para não revelar automaticamente:",
+  "settings.content_warnings_media_outside": "Exibir anexos de mídia fora avisos de conteúdo",
+  "settings.content_warnings_media_outside_hint": "Reproduzir o comportamento do Mastodonte, fazendo com que a alternância do Aviso de Conteúdo não afete os anexos de mídia",
+  "settings.content_warnings_shared_state": "Mostrar/ocultar o conteúdo de todas as cópias de uma só vez",
+  "settings.content_warnings_shared_state_hint": "Reproduzir o comportamento do Mastodonte fazendo com que o botão de Aviso de Conteúdo afete todas as cópias de um post de uma só vez. Isto evitará o colapso automático de qualquer cópia de um toon com Aviso de Conteúdo revelado",
+  "settings.content_warnings_unfold_opts": "Opções de auto-revelar",
+  "settings.deprecated_setting": "Essa configuração agora é controlada pelo {settings_page_link} do Mastodon",
+  "settings.enable_collapsed": "Habilitar toots recolhidos",
+  "settings.enable_collapsed_hint": "Posts recolhidos têm partes dos seus conteúdos ocultos para ocupar menos espaço na tela. Isto é diferente do recurso 'Aviso de Conteúdo'",
+  "settings.enable_content_warnings_auto_unfold": "Revelar automaticamente os avisos de conteúdo",
+  "settings.general": "Geral",
+  "settings.hicolor_privacy_icons": "Ícones de privacidade com cores de alto contraste",
+  "settings.hicolor_privacy_icons.hint": "Exibir ícones de privacidade em cores brilhantes e facilmente distinguíveis",
+  "settings.image_backgrounds": "Fundos de imagem",
+  "settings.image_backgrounds_media": "Pré-visualização da mídia de toots colapsados",
+  "settings.image_backgrounds_media_hint": "Se o post tiver algum anexo de mídia, use o primeiro em um plano de fundo",
+  "settings.image_backgrounds_users": "Dar a toots recolhidos uma imagem de fundo",
+  "settings.inline_preview_cards": "Cartões de pré-visualização em linha para links externos",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Opções de layout",
+  "settings.media": "Mídia",
+  "settings.media_fullwidth": "Pré-visualização da mídia em largura total",
+  "settings.media_letterbox": "Caixa de mensagens",
+  "settings.media_letterbox_hint": "Escala para baixo para encher os recipientes de imagem em vez de esticá-los e cortá-los",
+  "settings.media_reveal_behind_cw": "Revelar mídia sensível por trás de um Aviso de Conteúdo por padrão",
+  "settings.notifications.favicon_badge": "Notificações não lidas como emblema do favicon",
+  "settings.notifications.favicon_badge.hint": "Adicionar um emblema para notificações não lidas ao favicon",
+  "settings.notifications.tab_badge": "Emblema de notificações não lidas",
+  "settings.notifications.tab_badge.hint": "Exibir um emblema para notificações não lidas nos ícones de coluna quando a coluna de notificações não estiver aberta",
+  "settings.notifications_opts": "Opções de notificações",
+  "settings.pop_in_left": "Esquerda",
+  "settings.pop_in_player": "Ativar player pop-in",
+  "settings.pop_in_position": "Posição do player:",
+  "settings.pop_in_right": "Direita",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Preparar \"re: \" para avisos de conteúdo quando responder",
+  "settings.preselect_on_reply": "Nome de usuário pré-selecionado na resposta",
+  "settings.preselect_on_reply_hint": "Ao responder a uma conversa com vários participantes, pré-selecionar nomes de usuários após o primeiro",
+  "settings.rewrite_mentions": "Reescrever as menções nos status exibidos",
+  "settings.rewrite_mentions_acct": "Reescrever com nome de usuário e domínio (quando a conta for remota)",
+  "settings.rewrite_mentions_no": "Não reescrever menções",
+  "settings.rewrite_mentions_username": "Reescreva com nome de usuário",
+  "settings.shared_settings_link": "preferências do usuário",
+  "settings.show_action_bar": "Mostrar botões de ação em toots recolhidos",
+  "settings.show_content_type_choice": "Exibir opção do tipo de conteúdo ao autorar toots",
+  "settings.show_reply_counter": "Exibir uma estimativa da contagem de respostas",
+  "settings.side_arm": "Botão de toot secundário:",
+  "settings.side_arm.none": "Nenhum",
+  "settings.side_arm_reply_mode": "Ao responder a um toot, o botão secundário de toot deve:",
+  "settings.side_arm_reply_mode.copy": "Copiar configuração de privacidade do toot sendo respondido a",
+  "settings.side_arm_reply_mode.keep": "Mantenha sua privacidade definida",
+  "settings.side_arm_reply_mode.restrict": "Restringir configuração de privacidade ao toot sendo respondido a",
+  "settings.status_icons": "Ícones de toot",
+  "settings.status_icons_language": "Indicador de idioma",
+  "settings.status_icons_local_only": "Indicador somente local",
+  "settings.status_icons_media": "Indicadores de mídia e enquete",
+  "settings.status_icons_reply": "Indicador de resposta",
+  "settings.status_icons_visibility": "Indicador de privacidade",
+  "settings.swipe_to_change_columns": "Permitir deslizar para alterar colunas (apenas celular)",
+  "settings.tag_misleading_links": "Marcar links enganosos",
+  "settings.tag_misleading_links.hint": "Acrescentar uma indicação visual com o link hospedeiro alvo a cada link que não o mencione explicitamente",
+  "settings.wide_view": "Visualização ampla (apenas no Modo desktop)",
+  "settings.wide_view_hint": "Estica as colunas para preencher melhor o espaço disponível.",
+  "status.collapse": "Recolher",
+  "status.has_audio": "Possui um arquivo de áudio anexado",
+  "status.has_pictures": "Possui uma imagem anexada",
+  "status.has_preview_card": "Possui uma pré-visualização anexada",
+  "status.has_video": "Possui um vídeo anexado",
+  "status.in_reply_to": "Este toot é uma resposta",
+  "status.is_poll": "Este toot é uma enquete",
+  "status.local_only": "Visível apenas em sua instância",
+  "status.sensitive_toggle": "Clique para ver",
+  "status.uncollapse": "Revelar",
+  "web_app_crash.change_your_settings": "Altere suas {settings}",
+  "web_app_crash.content": "Você poderia tentar qualquer uma das seguintes opções:",
+  "web_app_crash.debug_info": "Informações de depuração",
+  "web_app_crash.disable_addons": "Desativar complementos do navegador ou ferramentas de tradução integradas",
+  "web_app_crash.issue_tracker": "rastreador de problemas",
+  "web_app_crash.reload": "Recarregar",
+  "web_app_crash.reload_page": "{reload} a página atual",
+  "web_app_crash.report_issue": "Relatar um erro no {issuetracker}",
+  "web_app_crash.settings": "configurações",
+  "web_app_crash.title": "Desculpe, mas algo deu errado com o aplicativo Mastodon."
+}
diff --git a/app/javascript/flavours/glitch/locales/pt-PT.js b/app/javascript/flavours/glitch/locales/pt-PT.js
deleted file mode 100644
index cf7afd17a..000000000
--- a/app/javascript/flavours/glitch/locales/pt-PT.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/pt-PT.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/pt-PT.json b/app/javascript/flavours/glitch/locales/pt-PT.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/pt-PT.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ro.js b/app/javascript/flavours/glitch/locales/ro.js
deleted file mode 100644
index a16446c6a..000000000
--- a/app/javascript/flavours/glitch/locales/ro.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ro.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ro.json b/app/javascript/flavours/glitch/locales/ro.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ro.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ru.js b/app/javascript/flavours/glitch/locales/ru.js
deleted file mode 100644
index 0e9f1de71..000000000
--- a/app/javascript/flavours/glitch/locales/ru.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ru.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ru.json b/app/javascript/flavours/glitch/locales/ru.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ru.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sa.js b/app/javascript/flavours/glitch/locales/sa.js
deleted file mode 100644
index 4cade0a07..000000000
--- a/app/javascript/flavours/glitch/locales/sa.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sa.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sa.json b/app/javascript/flavours/glitch/locales/sa.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sa.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sc.js b/app/javascript/flavours/glitch/locales/sc.js
deleted file mode 100644
index 88a83aa53..000000000
--- a/app/javascript/flavours/glitch/locales/sc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sc.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sc.json b/app/javascript/flavours/glitch/locales/sc.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sc.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sco.json b/app/javascript/flavours/glitch/locales/sco.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sco.json
@@ -0,0 +1 @@
+{}
diff --git a/app/javascript/flavours/glitch/locales/si.js b/app/javascript/flavours/glitch/locales/si.js
deleted file mode 100644
index d43266254..000000000
--- a/app/javascript/flavours/glitch/locales/si.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/si.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/si.json b/app/javascript/flavours/glitch/locales/si.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/si.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sk.js b/app/javascript/flavours/glitch/locales/sk.js
deleted file mode 100644
index 5fba6ab97..000000000
--- a/app/javascript/flavours/glitch/locales/sk.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sk.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sk.json b/app/javascript/flavours/glitch/locales/sk.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sk.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sl.js b/app/javascript/flavours/glitch/locales/sl.js
deleted file mode 100644
index c53c1bae8..000000000
--- a/app/javascript/flavours/glitch/locales/sl.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sl.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sl.json b/app/javascript/flavours/glitch/locales/sl.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sl.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sq.js b/app/javascript/flavours/glitch/locales/sq.js
deleted file mode 100644
index 2fb7a2973..000000000
--- a/app/javascript/flavours/glitch/locales/sq.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sq.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sq.json b/app/javascript/flavours/glitch/locales/sq.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sq.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sr-Latn.js b/app/javascript/flavours/glitch/locales/sr-Latn.js
deleted file mode 100644
index b42d5eaaf..000000000
--- a/app/javascript/flavours/glitch/locales/sr-Latn.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sr-Latn.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sr-Latn.json b/app/javascript/flavours/glitch/locales/sr-Latn.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sr-Latn.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sr.js b/app/javascript/flavours/glitch/locales/sr.js
deleted file mode 100644
index 8793d8d1e..000000000
--- a/app/javascript/flavours/glitch/locales/sr.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sr.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sr.json b/app/javascript/flavours/glitch/locales/sr.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sr.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/sv.js b/app/javascript/flavours/glitch/locales/sv.js
deleted file mode 100644
index b62c353fe..000000000
--- a/app/javascript/flavours/glitch/locales/sv.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/sv.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/sv.json b/app/javascript/flavours/glitch/locales/sv.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/sv.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/szl.js b/app/javascript/flavours/glitch/locales/szl.js
deleted file mode 100644
index 0b50afe45..000000000
--- a/app/javascript/flavours/glitch/locales/szl.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/szl.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/szl.json b/app/javascript/flavours/glitch/locales/szl.json
new file mode 100644
index 000000000..807ed8207
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/szl.json
@@ -0,0 +1,201 @@
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?",
+  "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": "{domain} is an \"instance\" of Mastodon. 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 {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.filters": "Filters",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/ta.js b/app/javascript/flavours/glitch/locales/ta.js
deleted file mode 100644
index d6ecdcb1b..000000000
--- a/app/javascript/flavours/glitch/locales/ta.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ta.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ta.json b/app/javascript/flavours/glitch/locales/ta.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ta.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/tai.js b/app/javascript/flavours/glitch/locales/tai.js
deleted file mode 100644
index f26cec5bd..000000000
--- a/app/javascript/flavours/glitch/locales/tai.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/tai.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/tai.json b/app/javascript/flavours/glitch/locales/tai.json
new file mode 100644
index 000000000..807ed8207
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/tai.json
@@ -0,0 +1,201 @@
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?",
+  "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": "{domain} is an \"instance\" of Mastodon. 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 {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.filters": "Filters",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/te.js b/app/javascript/flavours/glitch/locales/te.js
deleted file mode 100644
index afd6e4f7b..000000000
--- a/app/javascript/flavours/glitch/locales/te.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/te.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/te.json b/app/javascript/flavours/glitch/locales/te.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/te.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/th.js b/app/javascript/flavours/glitch/locales/th.js
deleted file mode 100644
index e939f8631..000000000
--- a/app/javascript/flavours/glitch/locales/th.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/th.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/th.json b/app/javascript/flavours/glitch/locales/th.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/th.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/tr.js b/app/javascript/flavours/glitch/locales/tr.js
deleted file mode 100644
index c2b740617..000000000
--- a/app/javascript/flavours/glitch/locales/tr.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/tr.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/tr.json b/app/javascript/flavours/glitch/locales/tr.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/tr.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/tt.js b/app/javascript/flavours/glitch/locales/tt.js
deleted file mode 100644
index ff74f6c29..000000000
--- a/app/javascript/flavours/glitch/locales/tt.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/tt.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/tt.json b/app/javascript/flavours/glitch/locales/tt.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/tt.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/ug.js b/app/javascript/flavours/glitch/locales/ug.js
deleted file mode 100644
index ab7ee0761..000000000
--- a/app/javascript/flavours/glitch/locales/ug.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ug.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ug.json b/app/javascript/flavours/glitch/locales/ug.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ug.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/uk.js b/app/javascript/flavours/glitch/locales/uk.js
deleted file mode 100644
index ab6d9a7dc..000000000
--- a/app/javascript/flavours/glitch/locales/uk.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/uk.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/uk.json b/app/javascript/flavours/glitch/locales/uk.json
new file mode 100644
index 000000000..b21584659
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/uk.json
@@ -0,0 +1,48 @@
+{
+  "advanced_options.local-only.long": "Не дмухати це на інші сервери",
+  "advanced_options.local-only.short": "Лише локальне",
+  "advanced_options.local-only.tooltip": "Цей дмух лише локальний",
+  "compose.attach": "Вкласти...",
+  "compose.attach.doodle": "Помалювати",
+  "compose.attach.upload": "Завантажити сюди файл",
+  "favourite_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
+  "getting_started.onboarding": "Шо тут",
+  "home.column_settings.show_direct": "Показати прямі повідомлення",
+  "layout.auto": "Автоматичний",
+  "layout.desktop": "Настільний",
+  "media_gallery.sensitive": "Чутливі",
+  "navigation_bar.app_settings": "Налаштування програми",
+  "notification.markForDeletion": "Позначити для видалення",
+  "notification_purge.btn_all": "Вибрати\nвсе",
+  "notification_purge.btn_apply": "Очистити\nвибір",
+  "notification_purge.btn_invert": "Інвертувати\nвибір",
+  "notification_purge.btn_none": "Вибрати\nнічого",
+  "notifications.marked_clear": "Очистити вибрані сповіщення",
+  "notifications.marked_clear_confirmation": "Ви впевнені, що хочете незворотньо очистити всі вибрані сповіщення?",
+  "onboarding.page_one.federation": "{domain} є сервером of Mastodon. Mastodon — мережа незалежних серверів, які працюють разом великою соціяльною мережою. Сервери Mastodon також називають „інстансами“.",
+  "onboarding.page_one.welcome": "Ласкаво просимо до {domain}!",
+  "onboarding.page_six.github": "{domain} використовує Glitchsoc. Glitchsoc — дружній {fork} {Mastodon}, сумісний з будь-яким сервером Mastodon або програмою для нього. Glitchsoc повністю вільний та відкритий. Повідомляти про баги, просити фічі, або працювати з кодом можна на {github}.",
+  "settings.auto_collapse": "Автоматичне згортання",
+  "settings.auto_collapse_all": "Все",
+  "settings.auto_collapse_lengthy": "Довгі дмухи",
+  "settings.auto_collapse_media": "Дмухи з медіафайлами",
+  "settings.auto_collapse_notifications": "Сповіщення",
+  "settings.auto_collapse_reblogs": "Передмухи",
+  "settings.auto_collapse_replies": "Відповіді",
+  "settings.close": "Закрити",
+  "settings.collapsed_statuses": "Згорнуті дмухи",
+  "settings.content_warnings": "Content warnings",
+  "settings.enable_collapsed": "Увімкути згорнутання дмухів",
+  "settings.general": "Основне",
+  "settings.image_backgrounds": "Картинки на тлі",
+  "settings.image_backgrounds_media": "Підглядати медіа зі схованих дмухів",
+  "settings.image_backgrounds_users": "Давати схованим дмухам тло-картинку",
+  "settings.media": "Медіа",
+  "settings.media_fullwidth": "Показувати медіа повною шириною",
+  "settings.media_letterbox": "Обрізати медіа",
+  "settings.preferences": "Користувацькі налаштування",
+  "settings.show_action_bar": "Показувати кнопки у згорнутих дмухах",
+  "settings.wide_view": "Широкий вид (тільки в режимі для комп'ютерів)",
+  "status.collapse": "Згорнути",
+  "status.uncollapse": "Розгорнути"
+}
diff --git a/app/javascript/flavours/glitch/locales/ur.js b/app/javascript/flavours/glitch/locales/ur.js
deleted file mode 100644
index 97ba291b0..000000000
--- a/app/javascript/flavours/glitch/locales/ur.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/ur.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/ur.json b/app/javascript/flavours/glitch/locales/ur.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/ur.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/vi.js b/app/javascript/flavours/glitch/locales/vi.js
deleted file mode 100644
index 499a96727..000000000
--- a/app/javascript/flavours/glitch/locales/vi.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/vi.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/vi.json b/app/javascript/flavours/glitch/locales/vi.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/vi.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/whitelist_af.json b/app/javascript/flavours/glitch/locales/whitelist_af.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_af.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ar.json b/app/javascript/flavours/glitch/locales/whitelist_ar.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ar.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ast.json b/app/javascript/flavours/glitch/locales/whitelist_ast.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ast.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_bg.json b/app/javascript/flavours/glitch/locales/whitelist_bg.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_bg.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_bn.json b/app/javascript/flavours/glitch/locales/whitelist_bn.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_bn.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_br.json b/app/javascript/flavours/glitch/locales/whitelist_br.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_br.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ca.json b/app/javascript/flavours/glitch/locales/whitelist_ca.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ca.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ckb.json b/app/javascript/flavours/glitch/locales/whitelist_ckb.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ckb.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_co.json b/app/javascript/flavours/glitch/locales/whitelist_co.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_co.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_cs.json b/app/javascript/flavours/glitch/locales/whitelist_cs.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_cs.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_cy.json b/app/javascript/flavours/glitch/locales/whitelist_cy.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_cy.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_da.json b/app/javascript/flavours/glitch/locales/whitelist_da.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_da.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_de.json b/app/javascript/flavours/glitch/locales/whitelist_de.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_de.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_el.json b/app/javascript/flavours/glitch/locales/whitelist_el.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_el.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_en.json b/app/javascript/flavours/glitch/locales/whitelist_en.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_en.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_eo.json b/app/javascript/flavours/glitch/locales/whitelist_eo.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_eo.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_es-AR.json b/app/javascript/flavours/glitch/locales/whitelist_es-AR.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_es-AR.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_es-MX.json b/app/javascript/flavours/glitch/locales/whitelist_es-MX.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_es-MX.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_es.json b/app/javascript/flavours/glitch/locales/whitelist_es.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_es.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_et.json b/app/javascript/flavours/glitch/locales/whitelist_et.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_et.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_eu.json b/app/javascript/flavours/glitch/locales/whitelist_eu.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_eu.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_fa.json b/app/javascript/flavours/glitch/locales/whitelist_fa.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_fa.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_fi.json b/app/javascript/flavours/glitch/locales/whitelist_fi.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_fi.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_fr.json b/app/javascript/flavours/glitch/locales/whitelist_fr.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_fr.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ga.json b/app/javascript/flavours/glitch/locales/whitelist_ga.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ga.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_gd.json b/app/javascript/flavours/glitch/locales/whitelist_gd.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_gd.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_gl.json b/app/javascript/flavours/glitch/locales/whitelist_gl.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_gl.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_he.json b/app/javascript/flavours/glitch/locales/whitelist_he.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_he.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_hi.json b/app/javascript/flavours/glitch/locales/whitelist_hi.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_hi.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_hr.json b/app/javascript/flavours/glitch/locales/whitelist_hr.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_hr.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_hu.json b/app/javascript/flavours/glitch/locales/whitelist_hu.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_hu.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_hy.json b/app/javascript/flavours/glitch/locales/whitelist_hy.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_hy.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_id.json b/app/javascript/flavours/glitch/locales/whitelist_id.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_id.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_io.json b/app/javascript/flavours/glitch/locales/whitelist_io.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_io.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_is.json b/app/javascript/flavours/glitch/locales/whitelist_is.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_is.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_it.json b/app/javascript/flavours/glitch/locales/whitelist_it.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_it.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ja.json b/app/javascript/flavours/glitch/locales/whitelist_ja.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ja.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ka.json b/app/javascript/flavours/glitch/locales/whitelist_ka.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ka.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_kab.json b/app/javascript/flavours/glitch/locales/whitelist_kab.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_kab.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_kk.json b/app/javascript/flavours/glitch/locales/whitelist_kk.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_kk.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_kn.json b/app/javascript/flavours/glitch/locales/whitelist_kn.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_kn.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ko.json b/app/javascript/flavours/glitch/locales/whitelist_ko.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ko.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ku.json b/app/javascript/flavours/glitch/locales/whitelist_ku.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ku.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_kw.json b/app/javascript/flavours/glitch/locales/whitelist_kw.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_kw.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_lt.json b/app/javascript/flavours/glitch/locales/whitelist_lt.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_lt.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_lv.json b/app/javascript/flavours/glitch/locales/whitelist_lv.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_lv.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_mk.json b/app/javascript/flavours/glitch/locales/whitelist_mk.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_mk.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ml.json b/app/javascript/flavours/glitch/locales/whitelist_ml.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ml.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_mr.json b/app/javascript/flavours/glitch/locales/whitelist_mr.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_mr.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ms.json b/app/javascript/flavours/glitch/locales/whitelist_ms.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ms.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_nl.json b/app/javascript/flavours/glitch/locales/whitelist_nl.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_nl.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_nn.json b/app/javascript/flavours/glitch/locales/whitelist_nn.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_nn.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_no.json b/app/javascript/flavours/glitch/locales/whitelist_no.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_no.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_oc.json b/app/javascript/flavours/glitch/locales/whitelist_oc.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_oc.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_pa.json b/app/javascript/flavours/glitch/locales/whitelist_pa.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_pa.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_pl.json b/app/javascript/flavours/glitch/locales/whitelist_pl.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_pl.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_pt-BR.json b/app/javascript/flavours/glitch/locales/whitelist_pt-BR.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_pt-BR.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_pt-PT.json b/app/javascript/flavours/glitch/locales/whitelist_pt-PT.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_pt-PT.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ro.json b/app/javascript/flavours/glitch/locales/whitelist_ro.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ro.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ru.json b/app/javascript/flavours/glitch/locales/whitelist_ru.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ru.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sa.json b/app/javascript/flavours/glitch/locales/whitelist_sa.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sa.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sc.json b/app/javascript/flavours/glitch/locales/whitelist_sc.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sc.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_si.json b/app/javascript/flavours/glitch/locales/whitelist_si.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_si.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sk.json b/app/javascript/flavours/glitch/locales/whitelist_sk.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sk.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sl.json b/app/javascript/flavours/glitch/locales/whitelist_sl.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sl.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sq.json b/app/javascript/flavours/glitch/locales/whitelist_sq.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sq.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sr-Latn.json b/app/javascript/flavours/glitch/locales/whitelist_sr-Latn.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sr-Latn.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sr.json b/app/javascript/flavours/glitch/locales/whitelist_sr.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sr.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sv.json b/app/javascript/flavours/glitch/locales/whitelist_sv.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sv.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_szl.json b/app/javascript/flavours/glitch/locales/whitelist_szl.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_szl.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ta.json b/app/javascript/flavours/glitch/locales/whitelist_ta.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ta.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_tai.json b/app/javascript/flavours/glitch/locales/whitelist_tai.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_tai.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_te.json b/app/javascript/flavours/glitch/locales/whitelist_te.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_te.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_th.json b/app/javascript/flavours/glitch/locales/whitelist_th.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_th.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_tr.json b/app/javascript/flavours/glitch/locales/whitelist_tr.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_tr.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_tt.json b/app/javascript/flavours/glitch/locales/whitelist_tt.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_tt.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ug.json b/app/javascript/flavours/glitch/locales/whitelist_ug.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ug.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_uk.json b/app/javascript/flavours/glitch/locales/whitelist_uk.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_uk.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ur.json b/app/javascript/flavours/glitch/locales/whitelist_ur.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ur.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_vi.json b/app/javascript/flavours/glitch/locales/whitelist_vi.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_vi.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_zgh.json b/app/javascript/flavours/glitch/locales/whitelist_zgh.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_zgh.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_zh-CN.json b/app/javascript/flavours/glitch/locales/whitelist_zh-CN.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_zh-CN.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_zh-HK.json b/app/javascript/flavours/glitch/locales/whitelist_zh-HK.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_zh-HK.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_zh-TW.json b/app/javascript/flavours/glitch/locales/whitelist_zh-TW.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_zh-TW.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/zgh.js b/app/javascript/flavours/glitch/locales/zgh.js
deleted file mode 100644
index f2f15b1a4..000000000
--- a/app/javascript/flavours/glitch/locales/zgh.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/zgh.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/zgh.json b/app/javascript/flavours/glitch/locales/zgh.json
new file mode 100644
index 000000000..807ed8207
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/zgh.json
@@ -0,0 +1,201 @@
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.info": "Extended information",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?",
+  "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": "{domain} is an \"instance\" of Mastodon. 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 {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.filters": "Filters",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/zh-CN.js b/app/javascript/flavours/glitch/locales/zh-CN.js
deleted file mode 100644
index 21a68fc01..000000000
--- a/app/javascript/flavours/glitch/locales/zh-CN.js
+++ /dev/null
@@ -1,201 +0,0 @@
-import inherited from 'mastodon/locales/zh-CN.json';
-
-const messages = {
-  'account.add_account_note': '为 @{name} 添加备注',
-  'account.disclaimer_full': '以下信息可能无法完整代表你的个人资料。',
-  'account.follows': '正在关注',
-  'account.suspended_disclaimer_full': '该用户已被封禁。',
-  'account.view_full_profile': '查看完整资料',
-  'account_note.cancel': '取消',
-  'account_note.edit': '编辑',
-  'account_note.glitch_placeholder': '暂无备注',
-  'account_note.save': '保存',
-  'advanced_options.icon_title': '高级选项',
-  'advanced_options.local-only.long': '不要发布嘟文到其他实例',
-  'advanced_options.local-only.short': '本地模式',
-  'advanced_options.local-only.tooltip': '这条嘟文仅限于本实例',
-  'advanced_options.threaded_mode.long': '发嘟时自动打开回复',
-  'advanced_options.threaded_mode.short': '线程模式',
-  'advanced_options.threaded_mode.tooltip': '线程模式已启用',
-  'boost_modal.missing_description': '这条嘟文未包含媒体描述',
-  'column.favourited_by': '喜欢',
-  'column.heading': '标题',
-  'column.reblogged_by': '转嘟',
-  'column.subheading': '其他选项',
-  'column.toot': '嘟文和回复',
-  'column_header.profile': '个人资料',
-  'column_subheading.lists': '列表',
-  'column_subheading.navigation': '导航',
-  'community.column_settings.allow_local_only': '只显示本地模式嘟文',
-  'compose.attach': '附上...',
-  'compose.attach.doodle': '画点什么',
-  'compose.attach.upload': '上传文件',
-  'compose.content-type.html': 'HTML',
-  'compose.content-type.markdown': 'Markdown',
-  'compose.content-type.plain': '纯文本',
-  'compose_form.poll.multiple_choices': '允许多选',
-  'compose_form.poll.single_choice': '允许单选',
-  'compose_form.spoiler': '隐藏为内容警告',
-  'confirmation_modal.do_not_ask_again': '下次不显示确认窗口',
-  'confirmations.discard_edit_media.confirm': '确认',
-  'confirmations.discard_edit_media.message': '有未保存的媒体描述或预览,确认要关闭?',
-  'confirmations.missing_media_description.confirm': '确认',
-  'confirmations.missing_media_description.edit': '编辑',
-  'confirmations.missing_media_description.message': '你没有为一种或多种媒体撰写描述。请考虑为视障人士添加描述。',
-  'confirmations.unfilter': '关于此过滤后嘟文的信息',
-  'confirmations.unfilter.author': '作者',
-  'confirmations.unfilter.confirm': '查看',
-  'confirmations.unfilter.edit_filter': '编辑过滤器',
-  'confirmations.unfilter.filters': '应用 {count, plural, one {过滤器} other {过滤器}}',
-  'content-type.change': '内容类型 ',
-  'direct.conversations_mode': '对话模式',
-  'direct.timeline_mode': '时间线模式',
-  'endorsed_accounts_editor.endorsed_accounts': '推荐用户',
-  'favourite_modal.combo': '下次你可以按 {combo} 跳过这个',
-  'getting_started.onboarding': '参观一下',
-  'getting_started.open_source_notice': 'Glitchsoc 是由 {Mastodon} 分叉出来的免费开源软件。你可以在 GitHub 上贡献或报告问题,地址是 {github}。',
-  'home.column_settings.advanced': '高级',
-  'home.column_settings.filter_regex': '按正则表达式过滤',
-  'home.column_settings.show_direct': '显示私信',
-  'home.settings': '列表设置',
-  'keyboard_shortcuts.bookmark': '书签',
-  'keyboard_shortcuts.secondary_toot': '使用二级隐私设置发送嘟文',
-  'keyboard_shortcuts.toggle_collapse': '折叠或展开嘟文',
-  'layout.auto': '自动模式',
-  'layout.current_is': '你目前的布局是:',
-  'layout.desktop': '桌面模式',
-  'layout.hint.auto': '根据“启用高级 Web 界面”设置和屏幕大小自动选择布局。',
-  'layout.hint.desktop': '“使用多列布局,无论“启用高级 Web 界面”设置和屏幕大小如何。',
-  'layout.hint.single': '使用单列布局,无论“启用高级 Web 界面”设置和屏幕大小如何。',
-  'layout.single': '移动模式',
-  'media_gallery.sensitive': '敏感内容',
-  'moved_to_warning': '此帐户已被标记为移至 {moved_to_link},并且似乎没有收到新关注者。',
-  'navigation_bar.app_settings': '应用选项',
-  'navigation_bar.featured_users': '推荐用户',
-  'navigation_bar.misc': '杂项',
-  'notification.markForDeletion': '标记以删除',
-  'notification_purge.btn_all': '全选',
-  'notification_purge.btn_apply': '清除已选',
-  'notification_purge.btn_invert': '反向选择',
-  'notification_purge.btn_none': '取消全选',
-  'notification_purge.start': '进入通知清除模式',
-  'notifications.clear': '清除所有通知',
-  'notifications.marked_clear': '清除选择的通知',
-  'notifications.marked_clear_confirmation': '你确定要永久清除所有选择的通知吗?',
-  'onboarding.done': '完成',
-  'onboarding.next': '下一个',
-  'onboarding.page_five.public_timelines': '本地时间线显示来自 {domain} 中所有人的公开嘟文。跨站时间线显示了 {domain} 用户关注的每个人的公开嘟文。这些被称为公共时间线,是发现新朋友的好方法。',
-  'onboarding.page_four.home': '你的主页时间线会显示你关注的人的嘟文。',
-  'onboarding.page_four.notifications': '通知栏显示某人与你互动的内容。',
-  'onboarding.page_one.federation': '{domain} 是 Mastodon 的一个“实例”。Mastodon 是一个由独立服务器组成的,通过不断联合形成的社交网络。我们称这些服务器为实例。',
-  'onboarding.page_one.handle': '你位于 {domain},因此你的完整用户名是 {handle} 。',
-  'onboarding.page_one.welcome': '欢迎来到 {domain}!',
-  'onboarding.page_six.admin': '实例的管理员是 {admin}。',
-  'onboarding.page_six.almost_done': '就快完成了...',
-  'onboarding.page_six.appetoot': '尽情享用吧!',
-  'onboarding.page_six.apps_available': '有适用于 iOS、Android 和其他平台的应用程序。',
-  'onboarding.page_six.github': '{domain} 在 Glitchsoc 上运行。Glitchsoc 是 {Mastodon} 的一个友好 {fork},与任何 Mastodon 实例或应用兼容。Glitchsoc 是完全免费和开源的。你可以在 {github} 上报告错误、请求功能或贡献代码。',
-  'onboarding.page_six.guidelines': '社区准则',
-  'onboarding.page_six.read_guidelines': '请阅读 {domain} 的 {guidelines}!',
-  'onboarding.page_six.various_app': '应用程序',
-  'onboarding.page_three.profile': '编辑你的个人资料,更改你的头像、个人简介和昵称。在那里,你还会发现其他设置。',
-  'onboarding.page_three.search': '使用搜索栏查找用户并查看标签,例如 #illustration 和 #introductions。要查找不在此实例中的用户,请使用他们的完整用户名。',
-  'onboarding.page_two.compose': '在撰写框中撰写嘟文。你可以使用下方图标上传图像、更改隐私设置和添加内容警告。',
-  'onboarding.skip': '跳过',
-  'settings.always_show_spoilers_field': '始终显示内容警告框',
-  'settings.auto_collapse': '自动折叠',
-  'settings.auto_collapse_all': '所有',
-  'settings.auto_collapse_lengthy': '长嘟文',
-  'settings.auto_collapse_media': '带媒体文件的嘟文',
-  'settings.auto_collapse_notifications': '通知',
-  'settings.auto_collapse_reblogs': '转嘟',
-  'settings.auto_collapse_replies': '回复',
-  'settings.close': '关闭',
-  'settings.collapsed_statuses': '折叠嘟文',
-  'settings.compose_box_opts': '撰写框',
-  'settings.confirm_before_clearing_draft': '在覆盖正在写入的嘟文之前显示确认对话框',
-  'settings.confirm_boost_missing_media_description': '在转嘟缺少媒体描述的嘟文之前显示确认对话框',
-  'settings.confirm_missing_media_description': '在发送缺少媒体描述的嘟文之前显示确认对话框',
-  'settings.content_warnings': '内容警告',
-  'settings.content_warnings.regexp': '正则表达式',
-  'settings.content_warnings_filter': '不会自动展开的内容警告:',
-  'settings.enable_collapsed': '启用折叠嘟文',
-  'settings.enable_content_warnings_auto_unfold': '自动展开内容警告',
-  'settings.filtering_behavior': '过滤器行为',
-  'settings.filtering_behavior.cw': '仍然显示嘟文,并在内容警告中添加过滤词',
-  'settings.filtering_behavior.drop': '完全隐藏过滤的嘟文',
-  'settings.filtering_behavior.hide': '显示“已过滤”并添加一个按钮来显示原因',
-  'settings.filtering_behavior.upstream': '像原版 Mastodon 一样显示“已过滤”',
-  'settings.filters': '过滤器',
-  'settings.general': '一般',
-  'settings.hicolor_privacy_icons': '彩色隐私图标 ',
-  'settings.hicolor_privacy_icons.hint': '以明亮且易于区分的颜色显示隐私图标',
-  'settings.image_backgrounds': '图片背景',
-  'settings.image_backgrounds_media': '预览折叠嘟文的媒体文件',
-  'settings.image_backgrounds_users': '为折叠嘟文附加图片背景',
-  'settings.inline_preview_cards': '外部链接的内嵌预览卡片',
-  'settings.layout': '布局:',
-  'settings.layout_opts': '布局选项',
-  'settings.media': '媒体',
-  'settings.media_fullwidth': '全宽媒体预览',
-  'settings.media_letterbox': '信箱媒体',
-  'settings.media_letterbox_hint': '缩小媒体以填充图像容器而不是拉伸和裁剪它们',
-  'settings.media_reveal_behind_cw': '默认显示内容警告后的敏感媒体',
-  'settings.navbar_under': '底部导航栏(仅限于移动模式)',
-  'settings.notifications.favicon_badge': '未读通知网站图标',
-  'settings.notifications.favicon_badge.hint': '将未读通知添加到网站图标',
-  'settings.notifications.tab_badge': '未读通知图标',
-  'settings.notifications.tab_badge.hint': '当通知栏未打开时,显示未读通知图标',
-  'settings.notifications_opts': '通知选项',
-  'settings.pop_in_left': '左边',
-  'settings.pop_in_player': '启用悬浮播放器',
-  'settings.pop_in_position': '悬浮播放器位置:',
-  'settings.pop_in_right': '右边',
-  'settings.preferences': '用户选项',
-  'settings.prepend_cw_re': '回复时在内容警告前加上“re:”',
-  'settings.preselect_on_reply': '回复时预先选择用户名',
-  'settings.preselect_on_reply_hint': '回复与多个参与者的对话时,预先选择第一个用户名',
-  'settings.rewrite_mentions': '重写嘟文中的提及',
-  'settings.rewrite_mentions_acct': '重写为用户名和域名(当帐户为远程时)',
-  'settings.rewrite_mentions_no': '不要重写',
-  'settings.rewrite_mentions_username': '重写为用户名',
-  'settings.show_action_bar': '在折叠的嘟文中显示操作按钮',
-  'settings.show_content_type_choice': '允许你在撰写嘟文时选择格式类型',
-  'settings.show_reply_counter': '显示回复的大致数量',
-  'settings.side_arm': '辅助发嘟按钮:',
-  'settings.side_arm.none': '无',
-  'settings.side_arm_reply_mode': '当回复嘟文时:',
-  'settings.side_arm_reply_mode.copy': '复制被回复嘟文的隐私设置',
-  'settings.side_arm_reply_mode.keep': '保留辅助发嘟按钮以设置隐私',
-  'settings.side_arm_reply_mode.restrict': '将隐私设置限制为正在回复的那条嘟文',
-  'settings.swipe_to_change_columns': '允许滑动以在列之间切换(仅限移动模式)',
-  'settings.tag_misleading_links': '标记误导性链接',
-  'settings.tag_misleading_links.hint': '将带有目标网页链接的视觉指示添加到每个未明确的链接',
-  'settings.wide_view': '宽视图(仅限于桌面模式)',
-  'settings.wide_view_hint': '拉伸列宽以更好地填充可用空间。',
-  'status.collapse': '折叠',
-  'status.has_audio': '附带音频文件',
-  'status.has_pictures': '附带图片文件',
-  'status.has_preview_card': '附带预览卡片',
-  'status.has_video': '附带视频文件',
-  'status.hide': '隐藏内容',
-  'status.in_reply_to': '此嘟文是回复',
-  'status.is_poll': '此嘟文是投票',
-  'status.local_only': '此嘟文仅本实例可见',
-  'status.sensitive_toggle': '点击查看',
-  'status.show_filter_reason': '(显示原因)',
-  'status.uncollapse': '不折叠',
-  'upload_modal.applying': '正在应用...',
-  'web_app_crash.change_your_settings': '更改 {settings}',
-  'web_app_crash.content': '你可以尝试这些:',
-  'web_app_crash.debug_info': '调试信息',
-  'web_app_crash.disable_addons': '禁用浏览器插件或本地翻译工具',
-  'web_app_crash.issue_tracker': '问题追踪器',
-  'web_app_crash.reload': '刷新',
-  'web_app_crash.reload_page': '{reload} 此页面',
-  'web_app_crash.report_issue': '将错误报告给 {issuetracker}',
-  'web_app_crash.settings': '设置',
-  'web_app_crash.title': '抱歉,Mastodon 出了点问题。',
-};
-
-export default Object.assign({}, inherited, messages);
\ No newline at end of file
diff --git a/app/javascript/flavours/glitch/locales/zh-CN.json b/app/javascript/flavours/glitch/locales/zh-CN.json
new file mode 100644
index 000000000..9b5a7713f
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/zh-CN.json
@@ -0,0 +1,176 @@
+{
+  "account.add_account_note": "为 @{name} 添加备注",
+  "account.disclaimer_full": "以下信息可能无法完整代表你的个人资料。",
+  "account.follows": "正在关注",
+  "account.suspended_disclaimer_full": "该用户已被封禁。",
+  "account.view_full_profile": "查看完整资料",
+  "account_note.cancel": "取消",
+  "account_note.edit": "编辑",
+  "account_note.glitch_placeholder": "暂无备注",
+  "account_note.save": "保存",
+  "advanced_options.icon_title": "高级选项",
+  "advanced_options.local-only.long": "不要发布嘟文到其他实例",
+  "advanced_options.local-only.short": "本地模式",
+  "advanced_options.local-only.tooltip": "这条嘟文仅限于本实例",
+  "advanced_options.threaded_mode.long": "发嘟时自动打开回复",
+  "advanced_options.threaded_mode.short": "线程模式",
+  "advanced_options.threaded_mode.tooltip": "线程模式已启用",
+  "boost_modal.missing_description": "这条嘟文未包含媒体描述",
+  "column.favourited_by": "喜欢",
+  "column.heading": "标题",
+  "column.reblogged_by": "转嘟",
+  "column.subheading": "其他选项",
+  "column_header.profile": "个人资料",
+  "column_subheading.lists": "列表",
+  "column_subheading.navigation": "导航",
+  "community.column_settings.allow_local_only": "只显示本地模式嘟文",
+  "compose.attach": "附上...",
+  "compose.attach.doodle": "画点什么",
+  "compose.attach.upload": "上传文件",
+  "compose.content-type.plain": "纯文本",
+  "compose_form.poll.multiple_choices": "允许多选",
+  "compose_form.poll.single_choice": "允许单选",
+  "compose_form.spoiler": "隐藏为内容警告",
+  "confirmation_modal.do_not_ask_again": "下次不显示确认窗口",
+  "confirmations.missing_media_description.confirm": "确认",
+  "confirmations.missing_media_description.edit": "编辑",
+  "confirmations.missing_media_description.message": "你没有为一种或多种媒体撰写描述。请考虑为视障人士添加描述。",
+  "confirmations.unfilter.author": "作者",
+  "confirmations.unfilter.confirm": "查看",
+  "confirmations.unfilter.edit_filter": "编辑过滤器",
+  "confirmations.unfilter.filters": "应用 {count, plural, one {过滤器} other {过滤器}}",
+  "content-type.change": "内容类型 ",
+  "endorsed_accounts_editor.endorsed_accounts": "推荐用户",
+  "favourite_modal.combo": "下次你可以按 {combo} 跳过这个",
+  "getting_started.onboarding": "参观一下",
+  "home.column_settings.advanced": "高级",
+  "home.column_settings.filter_regex": "按正则表达式过滤",
+  "home.column_settings.show_direct": "显示私信",
+  "home.settings": "列表设置",
+  "keyboard_shortcuts.bookmark": "书签",
+  "keyboard_shortcuts.secondary_toot": "使用二级隐私设置发送嘟文",
+  "keyboard_shortcuts.toggle_collapse": "折叠或展开嘟文",
+  "layout.auto": "自动模式",
+  "layout.desktop": "桌面模式",
+  "layout.hint.auto": "根据“启用高级 Web 界面”设置和屏幕大小自动选择布局。",
+  "layout.hint.desktop": "“使用多列布局,无论“启用高级 Web 界面”设置和屏幕大小如何。",
+  "layout.hint.single": "使用单列布局,无论“启用高级 Web 界面”设置和屏幕大小如何。",
+  "layout.single": "移动模式",
+  "media_gallery.sensitive": "敏感内容",
+  "moved_to_warning": "此帐户已被标记为移至 {moved_to_link},并且似乎没有收到新关注者。",
+  "navigation_bar.app_settings": "应用选项",
+  "navigation_bar.featured_users": "推荐用户",
+  "navigation_bar.misc": "杂项",
+  "notification.markForDeletion": "标记以删除",
+  "notification_purge.btn_all": "全选",
+  "notification_purge.btn_apply": "清除已选",
+  "notification_purge.btn_invert": "反向选择",
+  "notification_purge.btn_none": "取消全选",
+  "notification_purge.start": "进入通知清除模式",
+  "notifications.marked_clear": "清除选择的通知",
+  "notifications.marked_clear_confirmation": "你确定要永久清除所有选择的通知吗?",
+  "onboarding.done": "完成",
+  "onboarding.next": "下一个",
+  "onboarding.page_five.public_timelines": "本地时间线显示来自 {domain} 中所有人的公开嘟文。跨站时间线显示了 {domain} 用户关注的每个人的公开嘟文。这些被称为公共时间线,是发现新朋友的好方法。",
+  "onboarding.page_four.home": "你的主页时间线会显示你关注的人的嘟文。",
+  "onboarding.page_four.notifications": "通知栏显示某人与你互动的内容。",
+  "onboarding.page_one.federation": "{domain} 是 Mastodon 的一个“实例”。Mastodon 是一个由独立服务器组成的,通过不断联合形成的社交网络。我们称这些服务器为实例。",
+  "onboarding.page_one.handle": "你位于 {domain},因此你的完整用户名是 {handle} 。",
+  "onboarding.page_one.welcome": "欢迎来到 {domain}!",
+  "onboarding.page_six.admin": "实例的管理员是 {admin}。",
+  "onboarding.page_six.almost_done": "就快完成了...",
+  "onboarding.page_six.appetoot": "尽情享用吧!",
+  "onboarding.page_six.apps_available": "有适用于 iOS、Android 和其他平台的应用程序。",
+  "onboarding.page_six.github": "{domain} 在 Glitchsoc 上运行。Glitchsoc 是 {Mastodon} 的一个友好 {fork},与任何 Mastodon 实例或应用兼容。Glitchsoc 是完全免费和开源的。你可以在 {github} 上报告错误、请求功能或贡献代码。",
+  "onboarding.page_six.guidelines": "社区准则",
+  "onboarding.page_six.read_guidelines": "请阅读 {domain} 的 {guidelines}!",
+  "onboarding.page_six.various_app": "应用程序",
+  "onboarding.page_three.profile": "编辑你的个人资料,更改你的头像、个人简介和昵称。在那里,你还会发现其他设置。",
+  "onboarding.page_three.search": "使用搜索栏查找用户并查看标签,例如 #illustration 和 #introductions。要查找不在此实例中的用户,请使用他们的完整用户名。",
+  "onboarding.page_two.compose": "在撰写框中撰写嘟文。你可以使用下方图标上传图像、更改隐私设置和添加内容警告。",
+  "onboarding.skip": "跳过",
+  "settings.always_show_spoilers_field": "始终显示内容警告框",
+  "settings.auto_collapse": "自动折叠",
+  "settings.auto_collapse_all": "所有",
+  "settings.auto_collapse_lengthy": "长嘟文",
+  "settings.auto_collapse_media": "带媒体文件的嘟文",
+  "settings.auto_collapse_notifications": "通知",
+  "settings.auto_collapse_reblogs": "转嘟",
+  "settings.auto_collapse_replies": "回复",
+  "settings.close": "关闭",
+  "settings.collapsed_statuses": "折叠嘟文",
+  "settings.compose_box_opts": "撰写框",
+  "settings.confirm_before_clearing_draft": "在覆盖正在写入的嘟文之前显示确认对话框",
+  "settings.confirm_boost_missing_media_description": "在转嘟缺少媒体描述的嘟文之前显示确认对话框",
+  "settings.confirm_missing_media_description": "在发送缺少媒体描述的嘟文之前显示确认对话框",
+  "settings.content_warnings": "内容警告",
+  "settings.content_warnings.regexp": "正则表达式",
+  "settings.content_warnings_filter": "不会自动展开的内容警告:",
+  "settings.enable_collapsed": "启用折叠嘟文",
+  "settings.enable_content_warnings_auto_unfold": "自动展开内容警告",
+  "settings.general": "一般",
+  "settings.hicolor_privacy_icons": "彩色隐私图标 ",
+  "settings.hicolor_privacy_icons.hint": "以明亮且易于区分的颜色显示隐私图标",
+  "settings.image_backgrounds": "图片背景",
+  "settings.image_backgrounds_media": "预览折叠嘟文的媒体文件",
+  "settings.image_backgrounds_users": "为折叠嘟文附加图片背景",
+  "settings.inline_preview_cards": "外部链接的内嵌预览卡片",
+  "settings.layout": "布局:",
+  "settings.layout_opts": "布局选项",
+  "settings.media": "媒体",
+  "settings.media_fullwidth": "全宽媒体预览",
+  "settings.media_letterbox": "信箱媒体",
+  "settings.media_letterbox_hint": "缩小媒体以填充图像容器而不是拉伸和裁剪它们",
+  "settings.media_reveal_behind_cw": "默认显示内容警告后的敏感媒体",
+  "settings.notifications.favicon_badge": "未读通知网站图标",
+  "settings.notifications.favicon_badge.hint": "将未读通知添加到网站图标",
+  "settings.notifications.tab_badge": "未读通知图标",
+  "settings.notifications.tab_badge.hint": "当通知栏未打开时,显示未读通知图标",
+  "settings.notifications_opts": "通知选项",
+  "settings.pop_in_left": "左边",
+  "settings.pop_in_player": "启用悬浮播放器",
+  "settings.pop_in_position": "悬浮播放器位置:",
+  "settings.pop_in_right": "右边",
+  "settings.preferences": "用户选项",
+  "settings.prepend_cw_re": "回复时在内容警告前加上“re:”",
+  "settings.preselect_on_reply": "回复时预先选择用户名",
+  "settings.preselect_on_reply_hint": "回复与多个参与者的对话时,预先选择第一个用户名",
+  "settings.rewrite_mentions": "重写嘟文中的提及",
+  "settings.rewrite_mentions_acct": "重写为用户名和域名(当帐户为远程时)",
+  "settings.rewrite_mentions_no": "不要重写",
+  "settings.rewrite_mentions_username": "重写为用户名",
+  "settings.show_action_bar": "在折叠的嘟文中显示操作按钮",
+  "settings.show_content_type_choice": "允许你在撰写嘟文时选择格式类型",
+  "settings.show_reply_counter": "显示回复的大致数量",
+  "settings.side_arm": "辅助发嘟按钮:",
+  "settings.side_arm.none": "无",
+  "settings.side_arm_reply_mode": "当回复嘟文时:",
+  "settings.side_arm_reply_mode.copy": "复制被回复嘟文的隐私设置",
+  "settings.side_arm_reply_mode.keep": "保留辅助发嘟按钮以设置隐私",
+  "settings.side_arm_reply_mode.restrict": "将隐私设置限制为正在回复的那条嘟文",
+  "settings.swipe_to_change_columns": "允许滑动以在列之间切换(仅限移动模式)",
+  "settings.tag_misleading_links": "标记误导性链接",
+  "settings.tag_misleading_links.hint": "将带有目标网页链接的视觉指示添加到每个未明确的链接",
+  "settings.wide_view": "宽视图(仅限于桌面模式)",
+  "settings.wide_view_hint": "拉伸列宽以更好地填充可用空间。",
+  "status.collapse": "折叠",
+  "status.has_audio": "附带音频文件",
+  "status.has_pictures": "附带图片文件",
+  "status.has_preview_card": "附带预览卡片",
+  "status.has_video": "附带视频文件",
+  "status.in_reply_to": "此嘟文是回复",
+  "status.is_poll": "此嘟文是投票",
+  "status.local_only": "此嘟文仅本实例可见",
+  "status.sensitive_toggle": "点击查看",
+  "status.uncollapse": "不折叠",
+  "web_app_crash.change_your_settings": "更改 {settings}",
+  "web_app_crash.content": "你可以尝试这些:",
+  "web_app_crash.debug_info": "调试信息",
+  "web_app_crash.disable_addons": "禁用浏览器插件或本地翻译工具",
+  "web_app_crash.issue_tracker": "问题追踪器",
+  "web_app_crash.reload": "刷新",
+  "web_app_crash.reload_page": "{reload} 此页面",
+  "web_app_crash.report_issue": "将错误报告给 {issuetracker}",
+  "web_app_crash.settings": "设置",
+  "web_app_crash.title": "抱歉,Mastodon 出了点问题。"
+}
diff --git a/app/javascript/flavours/glitch/locales/zh-HK.js b/app/javascript/flavours/glitch/locales/zh-HK.js
deleted file mode 100644
index b71c81f2b..000000000
--- a/app/javascript/flavours/glitch/locales/zh-HK.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/zh-HK.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/zh-HK.json b/app/javascript/flavours/glitch/locales/zh-HK.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/zh-HK.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/locales/zh-TW.js b/app/javascript/flavours/glitch/locales/zh-TW.js
deleted file mode 100644
index de2b7769c..000000000
--- a/app/javascript/flavours/glitch/locales/zh-TW.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import inherited from 'mastodon/locales/zh-TW.json';
-
-const messages = {
-  //  No translations available.
-};
-
-export default Object.assign({}, inherited, messages);
diff --git a/app/javascript/flavours/glitch/locales/zh-TW.json b/app/javascript/flavours/glitch/locales/zh-TW.json
new file mode 100644
index 000000000..4d243f94c
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/zh-TW.json
@@ -0,0 +1,6 @@
+{
+  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "settings.content_warnings": "Content warnings",
+  "settings.preferences": "Preferences"
+}
diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.js
index 843fd5163..b256fdbd5 100644
--- a/app/javascript/flavours/glitch/packs/public.js
+++ b/app/javascript/flavours/glitch/packs/public.js
@@ -42,6 +42,18 @@ function main() {
       minute: 'numeric',
     });
 
+    const dateFormat = new Intl.DateTimeFormat(locale, {
+      year: 'numeric',
+      month: 'short',
+      day: 'numeric',
+      timeFormat: false,
+    });
+
+    const timeFormat = new Intl.DateTimeFormat(locale, {
+      timeStyle: 'short',
+      hour12: false,
+    });
+
     [].forEach.call(document.querySelectorAll('.emojify'), (content) => {
       content.innerHTML = emojify(content.innerHTML);
     });
@@ -54,6 +66,32 @@ function main() {
       content.textContent = formattedDate;
     });
 
+    const isToday = date => {
+      const today = new Date();
+
+      return date.getDate() === today.getDate() &&
+        date.getMonth() === today.getMonth() &&
+        date.getFullYear() === today.getFullYear();
+    };
+    const todayFormat = new IntlMessageFormat(messages['relative_format.today'] || 'Today at {time}', locale);
+
+    [].forEach.call(document.querySelectorAll('time.relative-formatted'), (content) => {
+      const datetime = new Date(content.getAttribute('datetime'));
+
+      let formattedContent;
+
+      if (isToday(datetime)) {
+        const formattedTime = timeFormat.format(datetime);
+
+        formattedContent = todayFormat.format({ time: formattedTime });
+      } else {
+        formattedContent = dateFormat.format(datetime);
+      }
+
+      content.title = formattedContent;
+      content.textContent = formattedContent;
+    });
+
     [].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
       const datetime = new Date(content.getAttribute('datetime'));
       const now      = new Date();
diff --git a/app/javascript/flavours/glitch/permissions.js b/app/javascript/flavours/glitch/permissions.js
index 752ddd6c5..9ea149e5f 100644
--- a/app/javascript/flavours/glitch/permissions.js
+++ b/app/javascript/flavours/glitch/permissions.js
@@ -1,3 +1,4 @@
-export const PERMISSION_INVITE_USERS   = 0x0000000000010000;
-export const PERMISSION_MANAGE_USERS   = 0x0000000000000400;
-export const PERMISSION_MANAGE_REPORTS = 0x0000000000000010;
+export const PERMISSION_INVITE_USERS      = 0x0000000000010000;
+export const PERMISSION_MANAGE_USERS      = 0x0000000000000400;
+export const PERMISSION_MANAGE_FEDERATION = 0x0000000000000020;
+export const PERMISSION_MANAGE_REPORTS    = 0x0000000000000010;
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 9b50ec23a..814b6a1a7 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -421,8 +421,10 @@ export default function compose(state = initialState, action) {
       map.set('preselectDate', new Date());
       map.set('idempotencyKey', uuid());
 
-      if (action.status.get('language')) {
+      if (action.status.get('language') && !action.status.has('translation')) {
         map.set('language', action.status.get('language'));
+      } else {
+        map.set('language', state.get('default_language'));
       }
 
       if (action.status.get('spoiler_text').length > 0) {
@@ -536,6 +538,8 @@ export default function compose(state = initialState, action) {
   case TIMELINE_DELETE:
     if (action.id === state.get('in_reply_to')) {
       return state.set('in_reply_to', null);
+    } else if (action.id === state.get('id')) {
+      return state.set('id', null);
     } else {
       return state;
     }
diff --git a/app/javascript/flavours/glitch/reducers/dropdown_menu.js b/app/javascript/flavours/glitch/reducers/dropdown_menu.js
index a78a11acc..51bf9375b 100644
--- a/app/javascript/flavours/glitch/reducers/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/reducers/dropdown_menu.js
@@ -4,12 +4,12 @@ import {
   DROPDOWN_MENU_CLOSE,
 } from '../actions/dropdown_menu';
 
-const initialState = Immutable.Map({ openId: null, placement: null, keyboard: false, scroll_key: null });
+const initialState = Immutable.Map({ openId: null, keyboard: false, scroll_key: null });
 
 export default function dropdownMenu(state = initialState, action) {
   switch (action.type) {
   case DROPDOWN_MENU_OPEN:
-    return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard, scroll_key: action.scroll_key });
+    return state.merge({ openId: action.id, keyboard: action.keyboard, scroll_key: action.scroll_key });
   case DROPDOWN_MENU_CLOSE:
     return state.get('openId') === action.id ? state.set('openId', null).set('scroll_key', null) : state;
   default:
diff --git a/app/javascript/flavours/glitch/reducers/relationships.js b/app/javascript/flavours/glitch/reducers/relationships.js
index 49dd77ef5..e4b9acea2 100644
--- a/app/javascript/flavours/glitch/reducers/relationships.js
+++ b/app/javascript/flavours/glitch/reducers/relationships.js
@@ -1,4 +1,7 @@
 import {
+  NOTIFICATIONS_UPDATE,
+} from '../actions/notifications';
+import {
   ACCOUNT_FOLLOW_SUCCESS,
   ACCOUNT_FOLLOW_REQUEST,
   ACCOUNT_FOLLOW_FAIL,
@@ -12,6 +15,8 @@ import {
   ACCOUNT_PIN_SUCCESS,
   ACCOUNT_UNPIN_SUCCESS,
   RELATIONSHIPS_FETCH_SUCCESS,
+  FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
+  FOLLOW_REQUEST_REJECT_SUCCESS,
 } from 'flavours/glitch/actions/accounts';
 import {
   DOMAIN_BLOCK_SUCCESS,
@@ -44,6 +49,12 @@ const initialState = ImmutableMap();
 
 export default function relationships(state = initialState, action) {
   switch(action.type) {
+  case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
+    return state.setIn([action.id, 'followed_by'], true).setIn([action.id, 'requested_by'], false);
+  case FOLLOW_REQUEST_REJECT_SUCCESS:
+    return state.setIn([action.id, 'followed_by'], false).setIn([action.id, 'requested_by'], false);
+  case NOTIFICATIONS_UPDATE:
+    return action.notification.type === 'follow_request' ? state.setIn([action.notification.account.id, 'requested_by'], true) : state;
   case ACCOUNT_FOLLOW_REQUEST:
     return state.getIn([action.id, 'following']) ? state : state.setIn([action.id, action.locked ? 'requested' : 'following'], true);
   case ACCOUNT_FOLLOW_FAIL:
diff --git a/app/javascript/flavours/glitch/selectors/index.js b/app/javascript/flavours/glitch/selectors/index.js
index df46b58a8..83f8783d9 100644
--- a/app/javascript/flavours/glitch/selectors/index.js
+++ b/app/javascript/flavours/glitch/selectors/index.js
@@ -1,6 +1,6 @@
 import escapeTextContentForBrowser from 'escape-html';
 import { createSelector } from 'reselect';
-import { List as ImmutableList } from 'immutable';
+import { List as ImmutableList, Map as ImmutableMap, is } from 'immutable';
 import { toServerSideType } from 'flavours/glitch/utils/filters';
 import { me } from 'flavours/glitch/initial_state';
 
@@ -74,6 +74,16 @@ export const makeGetStatus = () => {
   );
 };
 
+export const makeGetPictureInPicture = () => {
+  return createSelector([
+    (state, { id }) => state.get('picture_in_picture').statusId === id,
+    (state) => state.getIn(['meta', 'layout']) !== 'mobile',
+  ], (inUse, available) => ImmutableMap({
+    inUse: inUse && available,
+    available,
+  }));
+};
+
 const getAlertsBase = state => state.get('alerts');
 
 export const getAlerts = createSelector([getAlertsBase], (base) => {
diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss
index c2426944b..8ddf815c3 100644
--- a/app/javascript/flavours/glitch/styles/admin.scss
+++ b/app/javascript/flavours/glitch/styles/admin.scss
@@ -254,10 +254,8 @@ $content-width: 840px;
 
       &__actions {
         display: inline-flex;
-
-        & > :not(:first-child) {
-          margin-left: 5px;
-        }
+        flex-flow: wrap;
+        gap: 5px;
       }
 
       h2 small {
@@ -1681,6 +1679,15 @@ a.sparkline {
   box-sizing: border-box;
   min-height: 100%;
 
+  a {
+    color: $highlight-text-color;
+    text-decoration: none;
+
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+
   p {
     margin-bottom: 20px;
     unicode-bidi: plaintext;
diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss
index ac2d642a8..5b3e1db1b 100644
--- a/app/javascript/flavours/glitch/styles/components/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/components/accounts.scss
@@ -523,7 +523,6 @@
       display: block;
       flex: 0 0 auto;
       width: 94px;
-      margin-left: -2px;
 
       .account__avatar {
         background: darken($ui-base-color, 8%);
@@ -540,6 +539,7 @@
     margin-top: -55px;
     gap: 8px;
     overflow: hidden;
+    margin-left: -2px; // aligns the pfp with content below
 
     &__buttons {
       display: flex;
@@ -593,6 +593,10 @@
           font-weight: 400;
           overflow: hidden;
           text-overflow: ellipsis;
+
+          span {
+            user-select: all;
+          }
         }
       }
     }
@@ -752,3 +756,37 @@
     }
   }
 }
+
+.moved-account-banner,
+.follow-request-banner {
+  padding: 20px;
+  background: lighten($ui-base-color, 4%);
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  &__message {
+    color: $darker-text-color;
+    padding: 8px 0;
+    padding-top: 0;
+    padding-bottom: 4px;
+    font-size: 14px;
+    font-weight: 500;
+    text-align: center;
+    margin-bottom: 16px;
+  }
+  &__action {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    gap: 15px;
+    width: 100%;
+  }
+
+  .detailed-status__display-name {
+    margin-bottom: 0;
+  }
+}
+
+.follow-request-banner .button {
+  width: 100%;
+}
diff --git a/app/javascript/flavours/glitch/styles/components/compose_form.scss b/app/javascript/flavours/glitch/styles/components/compose_form.scss
index 72d3aad1d..aa2d52ed0 100644
--- a/app/javascript/flavours/glitch/styles/components/compose_form.scss
+++ b/app/javascript/flavours/glitch/styles/components/compose_form.scss
@@ -586,7 +586,6 @@
 }
 
 .privacy-dropdown__dropdown {
-  position: absolute;
   border-radius: 4px;
   box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
   background: $simple-background-color;
@@ -653,7 +652,6 @@
 
 .language-dropdown {
   &__dropdown {
-    position: absolute;
     background: $simple-background-color;
     box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
     border-radius: 4px;
diff --git a/app/javascript/flavours/glitch/styles/components/emoji.scss b/app/javascript/flavours/glitch/styles/components/emoji.scss
index 9dfee346a..c037e03f9 100644
--- a/app/javascript/flavours/glitch/styles/components/emoji.scss
+++ b/app/javascript/flavours/glitch/styles/components/emoji.scss
@@ -13,7 +13,7 @@
 
 .emoji-picker-dropdown__menu {
   background: $simple-background-color;
-  position: absolute;
+  position: relative;
   box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
   border-radius: 4px;
   margin-top: 5px;
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index 84aca2ebc..d50316366 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -141,6 +141,30 @@
     &:disabled {
       opacity: 0.5;
     }
+
+    &.button--confirmation {
+      color: $valid-value-color;
+      border-color: $valid-value-color;
+
+      &:active,
+      &:focus,
+      &:hover {
+        background: $valid-value-color;
+        color: $primary-text-color;
+      }
+    }
+
+    &.button--destructive {
+      color: $error-value-color;
+      border-color: $error-value-color;
+
+      &:active,
+      &:focus,
+      &:hover {
+        background: $error-value-color;
+        color: $primary-text-color;
+      }
+    }
   }
 
   &.button--block {
@@ -322,9 +346,8 @@
   }
 }
 
-.dropdown-menu {
-  position: absolute;
-  transform-origin: 50% 0;
+body > [data-popper-placement] {
+  z-index: 3;
 }
 
 .invisible {
@@ -508,6 +531,42 @@
   }
 }
 
+.dropdown-animation {
+  animation: dropdown 300ms cubic-bezier(0.1, 0.7, 0.1, 1);
+
+  @keyframes dropdown {
+    from {
+      opacity: 0;
+      transform: scaleX(0.85) scaleY(0.75);
+    }
+
+    to {
+      opacity: 1;
+      transform: scaleX(1) scaleY(1);
+    }
+  }
+
+  &.top {
+    transform-origin: bottom;
+  }
+
+  &.right {
+    transform-origin: left;
+  }
+
+  &.bottom {
+    transform-origin: top;
+  }
+
+  &.left {
+    transform-origin: right;
+  }
+
+  .reduce-motion & {
+    animation: none;
+  }
+}
+
 .dropdown {
   display: inline-block;
 }
@@ -576,36 +635,42 @@
 
 .dropdown-menu__arrow {
   position: absolute;
-  width: 0;
-  height: 0;
-  border: 0 solid transparent;
 
-  &.left {
-    right: -5px;
-    margin-top: -5px;
-    border-width: 5px 0 5px 5px;
-    border-left-color: $ui-secondary-color;
+  &::before {
+    content: '';
+    display: block;
+    width: 14px;
+    height: 5px;
+    background-color: $ui-secondary-color;
+    mask-image: url("data:image/svg+xml;utf8,<svg width='14' height='5' xmlns='http://www.w3.org/2000/svg'><path d='M7 0L0 5h14L7 0z' fill='white'/></svg>");
   }
 
   &.top {
     bottom: -5px;
-    margin-left: -7px;
-    border-width: 5px 7px 0;
-    border-top-color: $ui-secondary-color;
+
+    &::before {
+      transform: rotate(180deg);
+    }
+  }
+
+  &.right {
+    left: -9px;
+
+    &::before {
+      transform: rotate(-90deg);
+    }
   }
 
   &.bottom {
     top: -5px;
-    margin-left: -7px;
-    border-width: 0 7px 5px;
-    border-bottom-color: $ui-secondary-color;
   }
 
-  &.right {
-    left: -5px;
-    margin-top: -5px;
-    border-width: 5px 5px 5px 0;
-    border-right-color: $ui-secondary-color;
+  &.left {
+    right: -9px;
+
+    &::before {
+      transform: rotate(90deg);
+    }
   }
 }
 
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index 8ba8bec10..972e01e7d 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -37,7 +37,6 @@
 .modal-root__modal {
   pointer-events: auto;
   display: flex;
-  z-index: 9999;
 }
 
 .media-modal__zoom-button {
diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss
index 70af0f651..b8078bdb6 100644
--- a/app/javascript/flavours/glitch/styles/components/search.scss
+++ b/app/javascript/flavours/glitch/styles/components/search.scss
@@ -1,4 +1,5 @@
 .search {
+  margin-bottom: 10px;
   position: relative;
 }
 
diff --git a/app/javascript/flavours/glitch/styles/components/single_column.scss b/app/javascript/flavours/glitch/styles/components/single_column.scss
index 45d57aedd..74e5d0884 100644
--- a/app/javascript/flavours/glitch/styles/components/single_column.scss
+++ b/app/javascript/flavours/glitch/styles/components/single_column.scss
@@ -227,8 +227,7 @@
     height: calc(100% - 10px) !important;
   }
 
-  .getting-started__wrapper,
-  .search {
+  .getting-started__wrapper {
     margin-bottom: 10px;
   }
 
@@ -281,7 +280,7 @@
     }
   }
 
-  .ui__header {
+  .layout-single-column .ui__header {
     display: flex;
     background: $ui-base-color;
     border-bottom: 1px solid lighten($ui-base-color, 8%);
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index 64dbc3cf0..a46fb94b2 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -448,7 +448,6 @@
 
 .status__relative-time {
   display: inline-block;
-  flex-grow: 1;
   color: $dark-text-color;
   font-size: 14px;
   text-align: right;
@@ -598,6 +597,10 @@
   width: 23.15px;
 }
 
+.status__action-bar-spacer {
+  flex-grow: 1;
+}
+
 .detailed-status__action-bar-dropdown {
   flex: 1 1 auto;
   display: flex;
@@ -1075,7 +1078,7 @@ a.status-card.compact:hover {
       pointer-events: 0;
       width: 100%;
       height: 100%;
-      border-left: 2px solid $highlight-text-color;
+      border-left: 4px solid $highlight-text-color;
       pointer-events: none;
     }
   }
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
index 9fc1aed2a..2ec2da833 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
@@ -285,22 +285,8 @@ html {
 .dropdown-menu {
   background: $white;
 
-  &__arrow {
-    &.left {
-      border-left-color: $white;
-    }
-
-    &.top {
-      border-top-color: $white;
-    }
-
-    &.bottom {
-      border-bottom-color: $white;
-    }
-
-    &.right {
-      border-right-color: $white;
-    }
+  &__arrow::before {
+    background-color: $white;
   }
 
   &__item {
diff --git a/app/javascript/flavours/glitch/styles/modal.scss b/app/javascript/flavours/glitch/styles/modal.scss
index 6c6de4206..a333926dd 100644
--- a/app/javascript/flavours/glitch/styles/modal.scss
+++ b/app/javascript/flavours/glitch/styles/modal.scss
@@ -1,5 +1,5 @@
 .modal-layout {
-  background: $ui-base-color url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color($ui-base-lighter-color)}"/></svg>') repeat-x bottom fixed;
+  background: $ui-base-color url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color($ui-base-lighter-color)}33"/></svg>') repeat-x bottom fixed;
   display: flex;
   flex-direction: column;
   height: 100vh;
diff --git a/app/javascript/flavours/glitch/styles/polls.scss b/app/javascript/flavours/glitch/styles/polls.scss
index 0847c8f4c..43924829d 100644
--- a/app/javascript/flavours/glitch/styles/polls.scss
+++ b/app/javascript/flavours/glitch/styles/polls.scss
@@ -289,10 +289,10 @@
   color: $dark-text-color;
 
   &__chart {
-    background: rgba(darken($ui-primary-color, 14%), 0.2);
+    background: rgba(darken($ui-primary-color, 14%), 0.7);
 
     &.leading {
-      background: rgba($ui-highlight-color, 0.2);
+      background: rgba($ui-highlight-color, 0.5);
     }
   }
 }
diff --git a/app/javascript/flavours/glitch/theme.yml b/app/javascript/flavours/glitch/theme.yml
index e85dd74e1..2a2cf30b5 100644
--- a/app/javascript/flavours/glitch/theme.yml
+++ b/app/javascript/flavours/glitch/theme.yml
@@ -12,10 +12,10 @@ pack:
   home:
     filename: packs/home.js
     preload:
-    - flavours/glitch/async/compose
-    - flavours/glitch/async/getting_started
-    - flavours/glitch/async/home_timeline
-    - flavours/glitch/async/notifications
+      - flavours/glitch/async/compose
+      - flavours/glitch/async/getting_started
+      - flavours/glitch/async/home_timeline
+      - flavours/glitch/async/notifications
   mailer:
   modal:
   public: packs/public.js
@@ -24,10 +24,13 @@ pack:
 
 #  (OPTIONAL) The directory which contains localization files for
 #  the flavour, relative to this directory. The contents of this
-#  directory must be `.js` or `.json` files whose names correspond to
+#  directory must be `.json` files whose names correspond to
 #  language tags and whose default exports are a messages object.
 locales: locales
 
+#  (OPTIONAL) Which flavour to inherit locales from
+inherit_locales: vanilla
+
 #  (OPTIONAL) A file to use as the preview screenshot for the flavour,
 #  or an array thereof. These are the full path from `app/javascript/`.
 screenshot: flavours/glitch/images/glitch-preview.jpg