Merge branch 'develop' into 'fix/use-semantically-correct-buttons'
# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
655d985a82
16 changed files with 5794 additions and 1081 deletions
|
@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Added some missing unicode emoji
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form
|
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form
|
||||||
- Fixed timeline errors locking timelines
|
- Fixed timeline errors locking timelines
|
||||||
|
|
|
@ -114,7 +114,8 @@ const EmojiInput = {
|
||||||
showPicker: false,
|
showPicker: false,
|
||||||
temporarilyHideSuggestions: false,
|
temporarilyHideSuggestions: false,
|
||||||
keepOpen: false,
|
keepOpen: false,
|
||||||
disableClickOutside: false
|
disableClickOutside: false,
|
||||||
|
suggestions: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -124,21 +125,6 @@ const EmojiInput = {
|
||||||
padEmoji () {
|
padEmoji () {
|
||||||
return this.$store.getters.mergedConfig.padEmoji
|
return this.$store.getters.mergedConfig.padEmoji
|
||||||
},
|
},
|
||||||
suggestions () {
|
|
||||||
const firstchar = this.textAtCaret.charAt(0)
|
|
||||||
if (this.textAtCaret === firstchar) { return [] }
|
|
||||||
const matchedSuggestions = this.suggest(this.textAtCaret)
|
|
||||||
if (matchedSuggestions.length <= 0) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return take(matchedSuggestions, 5)
|
|
||||||
.map(({ imageUrl, ...rest }, index) => ({
|
|
||||||
...rest,
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
img: imageUrl || '',
|
|
||||||
highlighted: index === this.highlighted
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
showSuggestions () {
|
showSuggestions () {
|
||||||
return this.focused &&
|
return this.focused &&
|
||||||
this.suggestions &&
|
this.suggestions &&
|
||||||
|
@ -188,6 +174,23 @@ const EmojiInput = {
|
||||||
watch: {
|
watch: {
|
||||||
showSuggestions: function (newValue) {
|
showSuggestions: function (newValue) {
|
||||||
this.$emit('shown', newValue)
|
this.$emit('shown', newValue)
|
||||||
|
},
|
||||||
|
textAtCaret: async function (newWord) {
|
||||||
|
const firstchar = newWord.charAt(0)
|
||||||
|
this.suggestions = []
|
||||||
|
if (newWord === firstchar) return
|
||||||
|
const matchedSuggestions = await this.suggest(newWord)
|
||||||
|
// Async: cancel if textAtCaret has changed during wait
|
||||||
|
if (this.textAtCaret !== newWord) return
|
||||||
|
if (matchedSuggestions.length <= 0) return
|
||||||
|
this.suggestions = take(matchedSuggestions, 5)
|
||||||
|
.map(({ imageUrl, ...rest }) => ({
|
||||||
|
...rest,
|
||||||
|
img: imageUrl || ''
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
suggestions (newValue) {
|
||||||
|
this.$nextTick(this.resize)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
v-for="(suggestion, index) in suggestions"
|
v-for="(suggestion, index) in suggestions"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="autocomplete-item"
|
class="autocomplete-item"
|
||||||
:class="{ highlighted: suggestion.highlighted }"
|
:class="{ highlighted: index === highlighted }"
|
||||||
@click.stop.prevent="onClick($event, suggestion)"
|
@click.stop.prevent="onClick($event, suggestion)"
|
||||||
>
|
>
|
||||||
<span class="image">
|
<span class="image">
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { debounce } from 'lodash'
|
|
||||||
/**
|
/**
|
||||||
* suggest - generates a suggestor function to be used by emoji-input
|
* suggest - generates a suggestor function to be used by emoji-input
|
||||||
* data: object providing source information for specific types of suggestions:
|
* data: object providing source information for specific types of suggestions:
|
||||||
|
@ -11,19 +10,19 @@ import { debounce } from 'lodash'
|
||||||
* doesn't support user linking you can just provide only emoji.
|
* doesn't support user linking you can just provide only emoji.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const debounceUserSearch = debounce((data, input) => {
|
export default data => {
|
||||||
data.updateUsersList(input)
|
const emojiCurry = suggestEmoji(data.emoji)
|
||||||
}, 500)
|
const usersCurry = data.store && suggestUsers(data.store)
|
||||||
|
return input => {
|
||||||
export default data => input => {
|
const firstChar = input[0]
|
||||||
const firstChar = input[0]
|
if (firstChar === ':' && data.emoji) {
|
||||||
if (firstChar === ':' && data.emoji) {
|
return emojiCurry(input)
|
||||||
return suggestEmoji(data.emoji)(input)
|
}
|
||||||
|
if (firstChar === '@' && usersCurry) {
|
||||||
|
return usersCurry(input)
|
||||||
|
}
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
if (firstChar === '@' && data.users) {
|
|
||||||
return suggestUsers(data)(input)
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const suggestEmoji = emojis => input => {
|
export const suggestEmoji = emojis => input => {
|
||||||
|
@ -57,50 +56,75 @@ export const suggestEmoji = emojis => input => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const suggestUsers = data => input => {
|
export const suggestUsers = ({ dispatch, state }) => {
|
||||||
const noPrefix = input.toLowerCase().substr(1)
|
// Keep some persistent values in closure, most importantly for the
|
||||||
const users = data.users
|
// custom debounce to work. Lodash debounce does not return a promise.
|
||||||
|
let suggestions = []
|
||||||
|
let previousQuery = ''
|
||||||
|
let timeout = null
|
||||||
|
let cancelUserSearch = null
|
||||||
|
|
||||||
const newUsers = users.filter(
|
const userSearch = (query) => dispatch('searchUsers', { query })
|
||||||
user =>
|
const debounceUserSearch = (query) => {
|
||||||
user.screen_name.toLowerCase().startsWith(noPrefix) ||
|
cancelUserSearch && cancelUserSearch()
|
||||||
user.name.toLowerCase().startsWith(noPrefix)
|
return new Promise((resolve, reject) => {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
/* taking only 20 results so that sorting is a bit cheaper, we display
|
userSearch(query).then(resolve).catch(reject)
|
||||||
* only 5 anyway. could be inaccurate, but we ideally we should query
|
}, 300)
|
||||||
* backend anyway
|
cancelUserSearch = () => {
|
||||||
*/
|
clearTimeout(timeout)
|
||||||
).slice(0, 20).sort((a, b) => {
|
resolve([])
|
||||||
let aScore = 0
|
}
|
||||||
let bScore = 0
|
})
|
||||||
|
}
|
||||||
// Matches on screen name (i.e. user@instance) makes a priority
|
|
||||||
aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
|
return async input => {
|
||||||
bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
|
const noPrefix = input.toLowerCase().substr(1)
|
||||||
|
if (previousQuery === noPrefix) return suggestions
|
||||||
// Matches on name takes second priority
|
|
||||||
aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
|
suggestions = []
|
||||||
bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
|
previousQuery = noPrefix
|
||||||
|
// Fetch more and wait, don't fetch if there's the 2nd @ because
|
||||||
const diff = (bScore - aScore) * 10
|
// the backend user search can't deal with it.
|
||||||
|
// Reference semantics make it so that we get the updated data after
|
||||||
// Then sort alphabetically
|
// the await.
|
||||||
const nameAlphabetically = a.name > b.name ? 1 : -1
|
if (!noPrefix.includes('@')) {
|
||||||
const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1
|
await debounceUserSearch(noPrefix)
|
||||||
|
}
|
||||||
return diff + nameAlphabetically + screenNameAlphabetically
|
|
||||||
/* eslint-disable camelcase */
|
const newSuggestions = state.users.users.filter(
|
||||||
}).map(({ screen_name, name, profile_image_url_original }) => ({
|
user =>
|
||||||
displayText: screen_name,
|
user.screen_name.toLowerCase().startsWith(noPrefix) ||
|
||||||
detailText: name,
|
user.name.toLowerCase().startsWith(noPrefix)
|
||||||
imageUrl: profile_image_url_original,
|
).slice(0, 20).sort((a, b) => {
|
||||||
replacement: '@' + screen_name + ' '
|
let aScore = 0
|
||||||
}))
|
let bScore = 0
|
||||||
|
|
||||||
// BE search users to get more comprehensive results
|
// Matches on screen name (i.e. user@instance) makes a priority
|
||||||
if (data.updateUsersList) {
|
aScore += a.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
|
||||||
debounceUserSearch(data, noPrefix)
|
bScore += b.screen_name.toLowerCase().startsWith(noPrefix) ? 2 : 0
|
||||||
|
|
||||||
|
// Matches on name takes second priority
|
||||||
|
aScore += a.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
|
||||||
|
bScore += b.name.toLowerCase().startsWith(noPrefix) ? 1 : 0
|
||||||
|
|
||||||
|
const diff = (bScore - aScore) * 10
|
||||||
|
|
||||||
|
// Then sort alphabetically
|
||||||
|
const nameAlphabetically = a.name > b.name ? 1 : -1
|
||||||
|
const screenNameAlphabetically = a.screen_name > b.screen_name ? 1 : -1
|
||||||
|
|
||||||
|
return diff + nameAlphabetically + screenNameAlphabetically
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
}).map(({ screen_name, name, profile_image_url_original }) => ({
|
||||||
|
displayText: screen_name,
|
||||||
|
detailText: name,
|
||||||
|
imageUrl: profile_image_url_original,
|
||||||
|
replacement: '@' + screen_name + ' '
|
||||||
|
}))
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
|
suggestions = newSuggestions || []
|
||||||
|
return suggestions
|
||||||
}
|
}
|
||||||
return newUsers
|
|
||||||
/* eslint-enable camelcase */
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,8 +159,7 @@ const PostStatusForm = {
|
||||||
...this.$store.state.instance.emoji,
|
...this.$store.state.instance.emoji,
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.customEmoji
|
||||||
],
|
],
|
||||||
users: this.$store.state.users.users,
|
store: this.$store
|
||||||
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
emojiSuggestor () {
|
emojiSuggestor () {
|
||||||
|
|
|
@ -68,8 +68,7 @@ const ProfileTab = {
|
||||||
...this.$store.state.instance.emoji,
|
...this.$store.state.instance.emoji,
|
||||||
...this.$store.state.instance.customEmoji
|
...this.$store.state.instance.customEmoji
|
||||||
],
|
],
|
||||||
users: this.$store.state.users.users,
|
store: this.$store
|
||||||
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
emojiSuggestor () {
|
emojiSuggestor () {
|
||||||
|
@ -79,10 +78,7 @@ const ProfileTab = {
|
||||||
] })
|
] })
|
||||||
},
|
},
|
||||||
userSuggestor () {
|
userSuggestor () {
|
||||||
return suggestor({
|
return suggestor({ store: this.$store })
|
||||||
users: this.$store.state.users.users,
|
|
||||||
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
fieldsLimits () {
|
fieldsLimits () {
|
||||||
return this.$store.state.instance.fieldsLimits
|
return this.$store.state.instance.fieldsLimits
|
||||||
|
|
|
@ -134,14 +134,14 @@
|
||||||
"registration": {
|
"registration": {
|
||||||
"bio": "Priskribo",
|
"bio": "Priskribo",
|
||||||
"email": "Retpoŝtadreso",
|
"email": "Retpoŝtadreso",
|
||||||
"fullname": "Vidiga nomo",
|
"fullname": "Prezenta nomo",
|
||||||
"password_confirm": "Konfirmo de pasvorto",
|
"password_confirm": "Konfirmo de pasvorto",
|
||||||
"registration": "Registriĝo",
|
"registration": "Registriĝo",
|
||||||
"token": "Invita ĵetono",
|
"token": "Invita ĵetono",
|
||||||
"captcha": "TESTO DE HOMECO",
|
"captcha": "TESTO DE HOMECO",
|
||||||
"new_captcha": "Klaku la bildon por akiri novan teston",
|
"new_captcha": "Klaku la bildon por akiri novan teston",
|
||||||
"username_placeholder": "ekz. lain",
|
"username_placeholder": "ekz. lain",
|
||||||
"fullname_placeholder": "ekz. Lain Iwakura",
|
"fullname_placeholder": "ekz. Lain Ivakura",
|
||||||
"bio_placeholder": "ekz.\nSaluton, mi estas Lain.\nMi estas animea knabino vivanta en Japanujo. Eble vi konas min pro la retejo « Wired ».",
|
"bio_placeholder": "ekz.\nSaluton, mi estas Lain.\nMi estas animea knabino vivanta en Japanujo. Eble vi konas min pro la retejo « Wired ».",
|
||||||
"validations": {
|
"validations": {
|
||||||
"username_required": "ne povas resti malplena",
|
"username_required": "ne povas resti malplena",
|
||||||
|
@ -164,7 +164,7 @@
|
||||||
"blocks_tab": "Blokitoj",
|
"blocks_tab": "Blokitoj",
|
||||||
"btnRadius": "Butonoj",
|
"btnRadius": "Butonoj",
|
||||||
"cBlue": "Blua (respondi, aboni)",
|
"cBlue": "Blua (respondi, aboni)",
|
||||||
"cGreen": "Verda (kunhavigi)",
|
"cGreen": "Verda (diskonigi)",
|
||||||
"cOrange": "Oranĝa (ŝati)",
|
"cOrange": "Oranĝa (ŝati)",
|
||||||
"cRed": "Ruĝa (nuligi)",
|
"cRed": "Ruĝa (nuligi)",
|
||||||
"change_password": "Ŝanĝi pasvorton",
|
"change_password": "Ŝanĝi pasvorton",
|
||||||
|
@ -207,8 +207,8 @@
|
||||||
"import_theme": "Enlegi antaŭagordojn",
|
"import_theme": "Enlegi antaŭagordojn",
|
||||||
"inputRadius": "Enigaj kampoj",
|
"inputRadius": "Enigaj kampoj",
|
||||||
"checkboxRadius": "Markbutonoj",
|
"checkboxRadius": "Markbutonoj",
|
||||||
"instance_default": "(implicita: {value})",
|
"instance_default": "(originale: {value})",
|
||||||
"instance_default_simple": "(implicita)",
|
"instance_default_simple": "(originale)",
|
||||||
"interface": "Fasado",
|
"interface": "Fasado",
|
||||||
"interfaceLanguage": "Lingvo de fasado",
|
"interfaceLanguage": "Lingvo de fasado",
|
||||||
"invalid_theme_imported": "La elektita dosiero ne estas subtenata haŭto de Pleromo. Neniuj ŝanĝoj al via haŭto okazis.",
|
"invalid_theme_imported": "La elektita dosiero ne estas subtenata haŭto de Pleromo. Neniuj ŝanĝoj al via haŭto okazis.",
|
||||||
|
@ -219,7 +219,7 @@
|
||||||
"loop_video_silent_only": "Ripetadi nur filmojn sen sono (ekz. la «GIF-ojn» de Mastodon)",
|
"loop_video_silent_only": "Ripetadi nur filmojn sen sono (ekz. la «GIF-ojn» de Mastodon)",
|
||||||
"mutes_tab": "Silentigoj",
|
"mutes_tab": "Silentigoj",
|
||||||
"play_videos_in_modal": "Ludi filmojn en ŝpruca kadro",
|
"play_videos_in_modal": "Ludi filmojn en ŝpruca kadro",
|
||||||
"use_contain_fit": "Ne tondi la kunsendaĵon en bildetoj",
|
"use_contain_fit": "Ne pritondi bildetojn de kunsendaĵoj",
|
||||||
"name": "Nomo",
|
"name": "Nomo",
|
||||||
"name_bio": "Nomo kaj priskribo",
|
"name_bio": "Nomo kaj priskribo",
|
||||||
"new_password": "Nova pasvorto",
|
"new_password": "Nova pasvorto",
|
||||||
|
@ -265,7 +265,7 @@
|
||||||
"subject_line_email": "Kiel retpoŝto: «re: temo»",
|
"subject_line_email": "Kiel retpoŝto: «re: temo»",
|
||||||
"subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe",
|
"subject_line_mastodon": "Kiel Mastodon: kopii senŝanĝe",
|
||||||
"subject_line_noop": "Ne kopii",
|
"subject_line_noop": "Ne kopii",
|
||||||
"post_status_content_type": "Afiŝi specon de la enhavo de la stato",
|
"post_status_content_type": "Speco de enhavo de afiŝo",
|
||||||
"stop_gifs": "Movi GIF-bildojn dum ŝvebo de muso",
|
"stop_gifs": "Movi GIF-bildojn dum ŝvebo de muso",
|
||||||
"streaming": "Ŝalti memagan fluigon de novaj afiŝoj kiam vi vidas la supron de la paĝo",
|
"streaming": "Ŝalti memagan fluigon de novaj afiŝoj kiam vi vidas la supron de la paĝo",
|
||||||
"text": "Teksto",
|
"text": "Teksto",
|
||||||
|
@ -379,7 +379,7 @@
|
||||||
"hint": "Por ombroj vi ankaŭ povas uzi --variable kiel koloran valoron, por uzi variantojn de CSS3. Bonvolu rimarki, ke tiuokaze agordoj de maltravidebleco ne funkcios.",
|
"hint": "Por ombroj vi ankaŭ povas uzi --variable kiel koloran valoron, por uzi variantojn de CSS3. Bonvolu rimarki, ke tiuokaze agordoj de maltravidebleco ne funkcios.",
|
||||||
"filter_hint": {
|
"filter_hint": {
|
||||||
"always_drop_shadow": "Averto: ĉi tiu ombro ĉiam uzas {0} kiam la foliumilo tion subtenas.",
|
"always_drop_shadow": "Averto: ĉi tiu ombro ĉiam uzas {0} kiam la foliumilo tion subtenas.",
|
||||||
"drop_shadow_syntax": "{0} ne subtenas parametron {1} kaj ŝlosilvorton {2}.",
|
"drop_shadow_syntax": "{0} ne subtenas parametron {1} kaj ĉefvorton {2}.",
|
||||||
"avatar_inset": "Bonvolu rimarki, ke agordi ambaŭ internajn kaj eksterajn ombrojn por profilbildoj povas redoni neatenditajn rezultojn ĉe profilbildoj travideblaj.",
|
"avatar_inset": "Bonvolu rimarki, ke agordi ambaŭ internajn kaj eksterajn ombrojn por profilbildoj povas redoni neatenditajn rezultojn ĉe profilbildoj travideblaj.",
|
||||||
"spread_zero": "Ombroj kun vastigo > 0 aperos kvazaŭ ĝi estus fakte nulo",
|
"spread_zero": "Ombroj kun vastigo > 0 aperos kvazaŭ ĝi estus fakte nulo",
|
||||||
"inset_classic": "Internaj ombroj uzos {0}"
|
"inset_classic": "Internaj ombroj uzos {0}"
|
||||||
|
@ -394,7 +394,7 @@
|
||||||
"button": "Butono",
|
"button": "Butono",
|
||||||
"buttonHover": "Butono (je ŝvebo)",
|
"buttonHover": "Butono (je ŝvebo)",
|
||||||
"buttonPressed": "Butono (premita)",
|
"buttonPressed": "Butono (premita)",
|
||||||
"buttonPressedHover": "Butono (premita kaj je ŝvebo)",
|
"buttonPressedHover": "Butono (je premo kaj ŝvebo)",
|
||||||
"input": "Eniga kampo"
|
"input": "Eniga kampo"
|
||||||
},
|
},
|
||||||
"hintV3": "Kolorojn de ombroj vi ankaŭ povas skribi per la sistemo {0}."
|
"hintV3": "Kolorojn de ombroj vi ankaŭ povas skribi per la sistemo {0}."
|
||||||
|
@ -683,7 +683,7 @@
|
||||||
"replace": "Anstataŭigi",
|
"replace": "Anstataŭigi",
|
||||||
"reject": "Rifuzi",
|
"reject": "Rifuzi",
|
||||||
"ftl_removal": "Forigo el la historio de «La tuta konata reto»",
|
"ftl_removal": "Forigo el la historio de «La tuta konata reto»",
|
||||||
"keyword_policies": "Politiko pri ŝlosilvortoj"
|
"keyword_policies": "Politiko pri ĉefvortoj"
|
||||||
},
|
},
|
||||||
"federation": "Federado",
|
"federation": "Federado",
|
||||||
"mrf_policies_desc": "Politikoj de Mesaĝa ŝanĝilaro (MRF) efikas sur federa konduto de la nodo. La sekvaj politikoj estas ŝaltitaj:"
|
"mrf_policies_desc": "Politikoj de Mesaĝa ŝanĝilaro (MRF) efikas sur federa konduto de la nodo. La sekvaj politikoj estas ŝaltitaj:"
|
||||||
|
@ -739,8 +739,8 @@
|
||||||
"week_short": "{0}s",
|
"week_short": "{0}s",
|
||||||
"weeks": "{0} semajnoj",
|
"weeks": "{0} semajnoj",
|
||||||
"week": "{0} semajno",
|
"week": "{0} semajno",
|
||||||
"seconds_short": "{0}s",
|
"seconds_short": "{0}sek",
|
||||||
"second_short": "{0}s",
|
"second_short": "{0}sek",
|
||||||
"seconds": "{0} sekundoj",
|
"seconds": "{0} sekundoj",
|
||||||
"second": "{0} sekundo",
|
"second": "{0} sekundo",
|
||||||
"now_short": "nun",
|
"now_short": "nun",
|
||||||
|
@ -749,14 +749,14 @@
|
||||||
"month_short": "{0}m",
|
"month_short": "{0}m",
|
||||||
"months": "{0} monatoj",
|
"months": "{0} monatoj",
|
||||||
"month": "{0} monato",
|
"month": "{0} monato",
|
||||||
"minutes_short": "{0}m",
|
"minutes_short": "{0}min",
|
||||||
"minute_short": "{0}m",
|
"minute_short": "{0}min",
|
||||||
"minutes": "{0} minutoj",
|
"minutes": "{0} minutoj",
|
||||||
"minute": "{0} minuto",
|
"minute": "{0} minuto",
|
||||||
"in_past": "antaŭ {0}",
|
"in_past": "antaŭ {0}",
|
||||||
"in_future": "post {0}",
|
"in_future": "post {0}",
|
||||||
"hours_short": "{0}h",
|
"hours_short": "{0}hor",
|
||||||
"hour_short": "{0}h",
|
"hour_short": "{0}hor",
|
||||||
"hours": "{0} horoj",
|
"hours": "{0} horoj",
|
||||||
"hour": "{0} horo",
|
"hour": "{0} horo",
|
||||||
"days_short": "{0}t",
|
"days_short": "{0}t",
|
||||||
|
|
|
@ -104,7 +104,8 @@
|
||||||
"no_more_notifications": "No hay más notificaciones",
|
"no_more_notifications": "No hay más notificaciones",
|
||||||
"reacted_with": "reaccionó con {0}",
|
"reacted_with": "reaccionó con {0}",
|
||||||
"migrated_to": "migrado a",
|
"migrated_to": "migrado a",
|
||||||
"follow_request": "quiere seguirte"
|
"follow_request": "quiere seguirte",
|
||||||
|
"error": "Error obteniendo notificaciones:{0}"
|
||||||
},
|
},
|
||||||
"polls": {
|
"polls": {
|
||||||
"add_poll": "Añadir encuesta",
|
"add_poll": "Añadir encuesta",
|
||||||
|
@ -313,7 +314,7 @@
|
||||||
"hide_followers_count_description": "No mostrar el número de cuentas que me siguen",
|
"hide_followers_count_description": "No mostrar el número de cuentas que me siguen",
|
||||||
"show_admin_badge": "Mostrar la insignia de Administrador en mi perfil",
|
"show_admin_badge": "Mostrar la insignia de Administrador en mi perfil",
|
||||||
"show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
|
"show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil",
|
||||||
"nsfw_clickthrough": "Activar el clic para ocultar los adjuntos NSFW",
|
"nsfw_clickthrough": "Habilitar la ocultación de la imagen de vista previa del enlace y el adjunto para los estados NSFW por defecto",
|
||||||
"oauth_tokens": "Tokens de OAuth",
|
"oauth_tokens": "Tokens de OAuth",
|
||||||
"token": "Token",
|
"token": "Token",
|
||||||
"refresh_token": "Actualizar el token",
|
"refresh_token": "Actualizar el token",
|
||||||
|
@ -605,7 +606,8 @@
|
||||||
"up_to_date": "Actualizado",
|
"up_to_date": "Actualizado",
|
||||||
"no_more_statuses": "No hay más estados",
|
"no_more_statuses": "No hay más estados",
|
||||||
"no_statuses": "Sin estados",
|
"no_statuses": "Sin estados",
|
||||||
"reload": "Recargar"
|
"reload": "Recargar",
|
||||||
|
"error": "Error obteniendo la linea de tiempo:{0}"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"favorites": "Favoritos",
|
"favorites": "Favoritos",
|
||||||
|
@ -628,7 +630,9 @@
|
||||||
"copy_link": "Copiar el enlace al estado",
|
"copy_link": "Copiar el enlace al estado",
|
||||||
"status_unavailable": "Estado no disponible",
|
"status_unavailable": "Estado no disponible",
|
||||||
"bookmark": "Marcar",
|
"bookmark": "Marcar",
|
||||||
"unbookmark": "Desmarcar"
|
"unbookmark": "Desmarcar",
|
||||||
|
"status_deleted": "Esta entrada ha sido eliminada",
|
||||||
|
"nsfw": "NSFW (No apropiado para el trabajo)"
|
||||||
},
|
},
|
||||||
"user_card": {
|
"user_card": {
|
||||||
"approve": "Aprobar",
|
"approve": "Aprobar",
|
||||||
|
|
|
@ -390,5 +390,13 @@
|
||||||
"GiB": "GiB",
|
"GiB": "GiB",
|
||||||
"TiB": "TiB"
|
"TiB": "TiB"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"mrf": {
|
||||||
|
"keyword": {
|
||||||
|
"keyword_policies": "פוליסת מילות מפתח"
|
||||||
|
},
|
||||||
|
"federation": "פדרציה"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,8 @@
|
||||||
"follow_request": "vuole seguirti",
|
"follow_request": "vuole seguirti",
|
||||||
"no_more_notifications": "Fine delle notifiche",
|
"no_more_notifications": "Fine delle notifiche",
|
||||||
"migrated_to": "è migrato verso",
|
"migrated_to": "è migrato verso",
|
||||||
"reacted_with": "ha reagito con {0}"
|
"reacted_with": "ha reagito con {0}",
|
||||||
|
"error": "Errore nel caricare le notifiche: {0}"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"attachments": "Allegati",
|
"attachments": "Allegati",
|
||||||
|
@ -427,7 +428,8 @@
|
||||||
"repeated": "condiviso",
|
"repeated": "condiviso",
|
||||||
"no_statuses": "Nessun messaggio",
|
"no_statuses": "Nessun messaggio",
|
||||||
"no_more_statuses": "Fine dei messaggi",
|
"no_more_statuses": "Fine dei messaggi",
|
||||||
"reload": "Ricarica"
|
"reload": "Ricarica",
|
||||||
|
"error": "Errore nel caricare la sequenza: {0}"
|
||||||
},
|
},
|
||||||
"user_card": {
|
"user_card": {
|
||||||
"follow": "Segui",
|
"follow": "Segui",
|
||||||
|
@ -703,7 +705,8 @@
|
||||||
"delete_confirm": "Vuoi veramente eliminare questo messaggio?",
|
"delete_confirm": "Vuoi veramente eliminare questo messaggio?",
|
||||||
"unbookmark": "Rimuovi segnalibro",
|
"unbookmark": "Rimuovi segnalibro",
|
||||||
"bookmark": "Aggiungi segnalibro",
|
"bookmark": "Aggiungi segnalibro",
|
||||||
"status_deleted": "Questo messagio è stato cancellato"
|
"status_deleted": "Questo messagio è stato cancellato",
|
||||||
|
"nsfw": "Pruriginoso"
|
||||||
},
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"years_short": "{0}a",
|
"years_short": "{0}a",
|
||||||
|
|
|
@ -18,7 +18,13 @@
|
||||||
"generic_error": "Произошла ошибка",
|
"generic_error": "Произошла ошибка",
|
||||||
"optional": "не обязательно",
|
"optional": "не обязательно",
|
||||||
"show_less": "Показать меньше",
|
"show_less": "Показать меньше",
|
||||||
"show_more": "Показать больше"
|
"show_more": "Показать больше",
|
||||||
|
"peek": "Взглянуть",
|
||||||
|
"dismiss": "Закрыть",
|
||||||
|
"retry": "Попробуйте еще раз",
|
||||||
|
"error_retry": "Пожалуйста попробуйте еще раз",
|
||||||
|
"close": "Закрыть",
|
||||||
|
"loading": "Загрузка…"
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Войти",
|
"login": "Войти",
|
||||||
|
@ -468,7 +474,9 @@
|
||||||
"media_proxy": "Прокси для внешних вложений",
|
"media_proxy": "Прокси для внешних вложений",
|
||||||
"text_limit": "Лимит символов",
|
"text_limit": "Лимит символов",
|
||||||
"title": "Особенности",
|
"title": "Особенности",
|
||||||
"gopher": "Gopher"
|
"gopher": "Gopher",
|
||||||
|
"who_to_follow": "Предложения кого читать",
|
||||||
|
"pleroma_chat_messages": "Pleroma Чат"
|
||||||
},
|
},
|
||||||
"tool_tip": {
|
"tool_tip": {
|
||||||
"accept_follow_request": "Принять запрос на чтение",
|
"accept_follow_request": "Принять запрос на чтение",
|
||||||
|
@ -477,6 +485,7 @@
|
||||||
"image_cropper": {
|
"image_cropper": {
|
||||||
"save_without_cropping": "Сохранить не обрезая",
|
"save_without_cropping": "Сохранить не обрезая",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"crop_picture": "Обрезать картинку"
|
"crop_picture": "Обрезать картинку",
|
||||||
|
"cancel": "Отмена"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
99
src/i18n/uk.json
Normal file
99
src/i18n/uk.json
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
{
|
||||||
|
"general": {
|
||||||
|
"dismiss": "Закрити",
|
||||||
|
"close": "Закрити",
|
||||||
|
"verify": "Перевірити",
|
||||||
|
"confirm": "Підтвердити",
|
||||||
|
"enable": "Увімкнути",
|
||||||
|
"disable": "Вимкнути",
|
||||||
|
"cancel": "Скасувати",
|
||||||
|
"show_less": "Показати менше",
|
||||||
|
"show_more": "Показати більше",
|
||||||
|
"optional": "необов'язково",
|
||||||
|
"retry": "Спробуйте ще раз",
|
||||||
|
"error_retry": "Будь ласка, спробуйте ще раз",
|
||||||
|
"generic_error": "Виникла помилка",
|
||||||
|
"loading": "Завантаження…",
|
||||||
|
"more": "Більше",
|
||||||
|
"submit": "Відправити",
|
||||||
|
"apply": "Застосувати",
|
||||||
|
"peek": "Глянути"
|
||||||
|
},
|
||||||
|
"finder": {
|
||||||
|
"error_fetching_user": "Користувача не знайдено",
|
||||||
|
"find_user": "Знайти користувача"
|
||||||
|
},
|
||||||
|
"features_panel": {
|
||||||
|
"gopher": "Gopher",
|
||||||
|
"pleroma_chat_messages": "Чат Pleroma",
|
||||||
|
"chat": "Чат",
|
||||||
|
"who_to_follow": "Кого відстежувати",
|
||||||
|
"title": "Особливості",
|
||||||
|
"scope_options": "Параметри осягу",
|
||||||
|
"media_proxy": "Посередник медіа-даних",
|
||||||
|
"text_limit": "Ліміт символів"
|
||||||
|
},
|
||||||
|
"exporter": {
|
||||||
|
"processing": "Опрацьовую, скоро ви зможете завантажити файл",
|
||||||
|
"export": "Експорт"
|
||||||
|
},
|
||||||
|
"domain_mute_card": {
|
||||||
|
"unmute_progress": "Вимикаю…",
|
||||||
|
"unmute": "Вимкнути ігнорування",
|
||||||
|
"mute_progress": "Вмикаю…",
|
||||||
|
"mute": "Ігнорувати"
|
||||||
|
},
|
||||||
|
"shoutbox": {
|
||||||
|
"title": "Для воплів"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"staff": "Адміністрація",
|
||||||
|
"mrf": {
|
||||||
|
"simple": {
|
||||||
|
"media_nsfw_desc": "Даний інстанс примусово позначає вкладення з наступних інстансів як NSFW:",
|
||||||
|
"media_nsfw": "Примусове визначення вкладення як дратівливого",
|
||||||
|
"media_removal_desc": "Поточний інстанс видаляє вкладення на перелічених інстансах:",
|
||||||
|
"media_removal": "Видалення вкладень",
|
||||||
|
"ftl_removal_desc": "Цей інстанс видаляє перелічені інстанси з \"Усієї відомої мережі\":",
|
||||||
|
"ftl_removal": "Видалення з \"Вся відома мережа\"",
|
||||||
|
"quarantine_desc": "Поточний інстанс буде надсилати тільки публічні пости наступним інстансам:",
|
||||||
|
"quarantine": "Карантин",
|
||||||
|
"reject_desc": "Поточний інстанс не прийматиме повідомлення з перелічених інстансів:",
|
||||||
|
"accept": "Прийняти",
|
||||||
|
"reject": "Відхилити",
|
||||||
|
"accept_desc": "Поточний інстанс приймає повідомлення тільки з перелічених інстансів:",
|
||||||
|
"simple_policies": "Правила поточного інстансу"
|
||||||
|
},
|
||||||
|
"mrf_policies_desc": "Правила MRF розповсюджуються на данний інстанс. Наступні правила активні:",
|
||||||
|
"mrf_policies": "Активні правила MRF (модуль переписування повідомлень)",
|
||||||
|
"keyword": {
|
||||||
|
"is_replaced_by": "→",
|
||||||
|
"replace": "Замінити",
|
||||||
|
"reject": "Відхилити",
|
||||||
|
"ftl_removal": "Прибрати з федеративної стрічки",
|
||||||
|
"keyword_policies": "Політика щодо ключових слів"
|
||||||
|
},
|
||||||
|
"federation": "Федерація"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"hint": "Увійдіть, щоб доєднатися до дискусії",
|
||||||
|
"username": "Ім'я користувача",
|
||||||
|
"register": "Зареєструватись",
|
||||||
|
"password": "Пароль",
|
||||||
|
"logout": "Вийти",
|
||||||
|
"description": "Увійти за допомогою OAuth",
|
||||||
|
"login": "Увійти"
|
||||||
|
},
|
||||||
|
"importer": {
|
||||||
|
"error": "Під час імпортування файлу сталася помилка.",
|
||||||
|
"success": "Імпортовано успішно.",
|
||||||
|
"submit": "Відправити"
|
||||||
|
},
|
||||||
|
"image_cropper": {
|
||||||
|
"cancel": "Відмінити",
|
||||||
|
"save_without_cropping": "Зберегти не обрізаючи",
|
||||||
|
"crop_picture": "Обрізати малюнок",
|
||||||
|
"save": "Зберегти"
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"apply": "应用",
|
"apply": "应用",
|
||||||
"submit": "提交",
|
"submit": "发送",
|
||||||
"more": "更多",
|
"more": "更多",
|
||||||
"generic_error": "发生了一个错误",
|
"generic_error": "发生了一个错误",
|
||||||
"optional": "可选",
|
"optional": "可选",
|
||||||
|
@ -297,7 +297,7 @@
|
||||||
"hide_follows_description": "不要显示我所关注的人",
|
"hide_follows_description": "不要显示我所关注的人",
|
||||||
"hide_followers_description": "不要显示关注我的人",
|
"hide_followers_description": "不要显示关注我的人",
|
||||||
"show_admin_badge": "显示管理徽章",
|
"show_admin_badge": "显示管理徽章",
|
||||||
"show_moderator_badge": "显示版主徽章",
|
"show_moderator_badge": "在我的个人资料中显示监察员标志",
|
||||||
"nsfw_clickthrough": "将不和谐附件隐藏,点击才能打开",
|
"nsfw_clickthrough": "将不和谐附件隐藏,点击才能打开",
|
||||||
"oauth_tokens": "OAuth令牌",
|
"oauth_tokens": "OAuth令牌",
|
||||||
"token": "令牌",
|
"token": "令牌",
|
||||||
|
@ -655,8 +655,8 @@
|
||||||
"moderation": "权限",
|
"moderation": "权限",
|
||||||
"grant_admin": "赋予管理权限",
|
"grant_admin": "赋予管理权限",
|
||||||
"revoke_admin": "撤销管理权限",
|
"revoke_admin": "撤销管理权限",
|
||||||
"grant_moderator": "赋予版主权限",
|
"grant_moderator": "赋予监察员权限",
|
||||||
"revoke_moderator": "撤销版主权限",
|
"revoke_moderator": "撤销监察员权限",
|
||||||
"activate_account": "激活账号",
|
"activate_account": "激活账号",
|
||||||
"deactivate_account": "关闭账号",
|
"deactivate_account": "关闭账号",
|
||||||
"delete_account": "删除账号",
|
"delete_account": "删除账号",
|
||||||
|
@ -683,7 +683,7 @@
|
||||||
},
|
},
|
||||||
"user_reporting": {
|
"user_reporting": {
|
||||||
"title": "报告 {0}",
|
"title": "报告 {0}",
|
||||||
"add_comment_description": "此报告会发送给您的实例管理员。您可以在下面提供更多详细信息解释报告的缘由:",
|
"add_comment_description": "此报告会发送给您的实例监察员。您可以在下面提供更多详细信息解释报告的缘由:",
|
||||||
"additional_comments": "其它信息",
|
"additional_comments": "其它信息",
|
||||||
"forward_description": "这个账号是从另外一个服务器。同时发送一个副本到那里?",
|
"forward_description": "这个账号是从另外一个服务器。同时发送一个副本到那里?",
|
||||||
"forward_to": "转发 {0}",
|
"forward_to": "转发 {0}",
|
||||||
|
|
2398
static/emoji.json
2398
static/emoji.json
File diff suppressed because it is too large
Load diff
67
tools/emoji_merger.js
Normal file
67
tools/emoji_merger.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Emoji merger script, quick hack of a tool to:
|
||||||
|
- update some missing emoji from an external source
|
||||||
|
- sort the emoji
|
||||||
|
- remove all multipart emoji (reactions don't allow them)
|
||||||
|
|
||||||
|
Merges emoji from here: https://gist.github.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb
|
||||||
|
to the simpler format we're using.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Existing emojis we have
|
||||||
|
const oldEmojiFilename = '../static/emoji.json'
|
||||||
|
|
||||||
|
// The file downloaded from https://gist.github.com/oliveratgithub/0bf11a9aff0d6da7b46f1490f86a71eb
|
||||||
|
const newEmojiFilename = 'emojis.json'
|
||||||
|
|
||||||
|
// Output, replace the static/emoji.json with this file if it looks correct
|
||||||
|
const outputFilename = 'output.json'
|
||||||
|
|
||||||
|
const run = () => {
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
let newEmojisObject = {}
|
||||||
|
let emojisObject = {}
|
||||||
|
|
||||||
|
let data = fs.readFileSync(newEmojiFilename, 'utf8')
|
||||||
|
|
||||||
|
// First filter out anything that's more than one codepoint
|
||||||
|
const newEmojis = JSON.parse(data).emojis.filter(e => e.emoji.length <= 2)
|
||||||
|
|
||||||
|
// Create a table with format { shortname: emoji }, remove the :
|
||||||
|
newEmojis.forEach(e => {
|
||||||
|
const name = e.shortname.slice(1, e.shortname.length - 1).toLowerCase()
|
||||||
|
if (name.length > 0) {
|
||||||
|
newEmojisObject[name] = e.emoji
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
data = fs.readFileSync(oldEmojiFilename, 'utf8')
|
||||||
|
|
||||||
|
emojisObject = JSON.parse(data)
|
||||||
|
|
||||||
|
// Get rid of longer emojis that don't play nice with reactions
|
||||||
|
Object.keys(emojisObject).forEach(e => {
|
||||||
|
if (emojisObject[e].length > 2) emojisObject[e] = undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add new emojis from the new tables to the old table
|
||||||
|
Object.keys(newEmojisObject).forEach(e => {
|
||||||
|
if (!emojisObject[e] && newEmojisObject[e].length <= 2) {
|
||||||
|
emojisObject[e] = newEmojisObject[e]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Sort by key
|
||||||
|
const sorted = Object.keys(emojisObject).sort().reduce((acc, key) => {
|
||||||
|
if (key.length === 0) return acc
|
||||||
|
acc[key] = emojisObject[key]
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
fs.writeFile(outputFilename, JSON.stringify(sorted, null, 2), 'utf8', (err) => {
|
||||||
|
if (err) console.log('Error writing file', err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
4036
tools/emojis.json
Normal file
4036
tools/emojis.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue