this repo has no description
0
fork

Configure Feed

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

Time to migrate away from Lingva

+1274 -402
+2 -1
.env
··· 1 1 PHANPY_CLIENT_NAME=Phanpy 2 2 PHANPY_WEBSITE=https://phanpy.social 3 3 PHANPY_LINGVA_INSTANCES="lingva.phanpy.social lingva.lunar.icu lingva.garudalinux.org translate.plausibility.cloud" 4 - PHANPY_PRIVACY_POLICY_URL="https://github.com/cheeaun/phanpy/blob/main/PRIVACY.MD" 4 + PHANPY_PRIVACY_POLICY_URL="https://github.com/cheeaun/phanpy/blob/main/PRIVACY.MD" 5 + PHANPY_TRANSLANG_INSTANCES="translang.phanpy.social"
+5 -1
README.md
··· 238 238 - This is applied with the `<meta>` tag on the client-side. 239 239 - The policy can also be set with `Referrer-Policy` header configured on the server-side (not this variable). 240 240 - Note that since Phanpy uses hash-based URLs, the referrer does not include the hash part. 241 - - `PHANPY_LINGVA_INSTANCES` (optional, space-separated list, default: `lingva.phanpy.social [...hard-coded list of fallback instances]`): 241 + - `PHANPY_LINGVA_INSTANCES` (**DEPRECATED**, optional, space-separated list, default: `lingva.phanpy.social [...hard-coded list of fallback instances]`): 242 242 - Specify a space-separated list of instances. First will be used as default before falling back to the subsequent instances. If there's only 1 instance, means no fallback. 243 243 - May specify a self-hosted Lingva instance, powered by either [lingva-translate](https://github.com/thedaviddelta/lingva-translate) or [lingva-api](https://github.com/cheeaun/lingva-api) 244 244 - List of fallback instances hard-coded in `/.env` 245 245 - [↗️ List of lingva-translate instances](https://github.com/thedaviddelta/lingva-translate?tab=readme-ov-file#instances) 246 + - `PHANPY_TRANSLANG_INSTANCES` (optional, space-separated list, default: `translang.phanpy.social`): 247 + - Specify a space-separated list of instances. First will be used as default before falling back to the subsequent instances. If there's only 1 instance, means no fallback. 248 + - May specify a self-hosted Translating instance, powered by [translang-api](https://github.com/cheeaun/translang-api). 249 + - List of instances hard-coded in `/.env` 246 250 - `PHANPY_IMG_ALT_API_URL` (optional, no defaults): 247 251 - API endpoint for self-hosted instance of [img-alt-api](https://github.com/cheeaun/img-alt-api). 248 252 - If provided, a setting will appear for users to enable the image description generator in the composer. Disabled by default.
+9
scripts/fetch-translang-languages.js
··· 1 + import fs from 'fs'; 2 + 3 + fetch('https://translang.phanpy.social/api/v1/languages') 4 + .then((response) => response.json()) 5 + .then((json) => { 6 + const file = './src/data/translang-languages.json'; 7 + console.log(`Writing ${file}...`); 8 + fs.writeFileSync(file, JSON.stringify(json, null, '\t'), 'utf8'); 9 + });
+24 -5
src/components/status.jsx
··· 119 119 ) 120 120 .join('\n')}`; 121 121 } 122 - function getPostText(status) { 123 - const { spoilerText, content, poll } = status; 122 + function getPostText(status, opts) { 123 + const { maskCustomEmojis } = opts || {}; 124 + const { spoilerText, poll, emojis } = status; 125 + let { content } = status; 126 + if (maskCustomEmojis && emojis?.length) { 127 + const emojisRegex = new RegExp( 128 + `:(${emojis.map((e) => e.shortcode).join('|')}):`, 129 + 'g', 130 + ); 131 + content = content.replace(emojisRegex, '⬚'); 132 + } 124 133 return ( 125 134 (spoilerText ? `${spoilerText}\n\n` : '') + 126 135 getHTMLText(content) + ··· 139 148 return []; 140 149 } 141 150 142 - function isTranslateble(content) { 151 + function isTranslateble(content, emojis) { 143 152 if (!content) return false; 153 + // Remove custom emojis 154 + if (emojis?.length) { 155 + const emojisRegex = new RegExp( 156 + `:(${emojis.map((e) => e.shortcode).join('|')}):`, 157 + 'g', 158 + ); 159 + content = content.replace(emojisRegex, ''); 160 + } 144 161 content = content.trim(); 145 162 if (!content) return false; 146 163 const text = getHTMLText(content, { ··· 2212 2229 /> 2213 2230 )} 2214 2231 {(((enableTranslate || inlineTranslate) && 2215 - isTranslateble(content) && 2232 + isTranslateble(content, emojis) && 2216 2233 differentLanguage) || 2217 2234 forceTranslate) && ( 2218 2235 <TranslationBlock ··· 2220 2237 mini={!isSizeLarge && !withinContext} 2221 2238 sourceLanguage={language} 2222 2239 autoDetected={languageAutoDetected} 2223 - text={getPostText(status)} 2240 + text={getPostText(status, { 2241 + maskCustomEmojis: true, 2242 + })} 2224 2243 /> 2225 2244 )} 2226 2245 {!previewMode &&
+57 -37
src/components/translation-block.jsx
··· 5 5 import pThrottle from 'p-throttle'; 6 6 import { useEffect, useRef, useState } from 'preact/hooks'; 7 7 8 - import sourceLanguages from '../data/lingva-source-languages'; 8 + import languages from '../data/translang-languages'; 9 9 import { 10 10 translate as browserTranslate, 11 11 supportsBrowserTranslator, ··· 18 18 import LazyShazam from './lazy-shazam'; 19 19 import Loader from './loader'; 20 20 21 - const { PHANPY_LINGVA_INSTANCES } = import.meta.env; 22 - const LINGVA_INSTANCES = PHANPY_LINGVA_INSTANCES 23 - ? PHANPY_LINGVA_INSTANCES.split(/\s+/) 21 + const sourceLanguages = Object.entries(languages.sl).map(([code, name]) => ({ 22 + code, 23 + name, 24 + })); 25 + 26 + const { PHANPY_TRANSLANG_INSTANCES } = import.meta.env; 27 + const TRANSLANG_INSTANCES = PHANPY_TRANSLANG_INSTANCES 28 + ? PHANPY_TRANSLANG_INSTANCES.split(/\s+/) 24 29 : []; 25 30 26 31 const throttle = pThrottle({ ··· 28 33 interval: 2000, 29 34 }); 30 35 31 - let currentLingvaInstance = 0; 36 + const TRANSLATED_MAX_AGE = 1000 * 60 * 60; // 1 hour 37 + let currentTranslangInstance = 0; 32 38 33 - function _lingvaTranslate(text, source, target) { 39 + function _translangTranslate(text, source, target) { 34 40 console.log('TRANSLATE', text, source, target); 35 41 const fetchCall = () => { 36 - let instance = LINGVA_INSTANCES[currentLingvaInstance]; 37 - return fetch( 38 - `https://${instance}/api/v1/${source}/${target}/${encodeURIComponent( 39 - text, 40 - )}`, 41 - ) 42 + let instance = TRANSLANG_INSTANCES[currentTranslangInstance]; 43 + const tooLong = text.length > 2000; 44 + let fetchPromise; 45 + if (tooLong) { 46 + // POST 47 + fetchPromise = fetch(`https://${instance}/api/v1/translate`, { 48 + method: 'POST', 49 + priority: 'low', 50 + referrerPolicy: 'no-referrer', 51 + headers: { 52 + 'Content-Type': 'application/json', 53 + }, 54 + body: JSON.stringify({ 55 + sl: source, 56 + tl: target, 57 + text, 58 + }), 59 + }); 60 + } else { 61 + // GET 62 + fetchPromise = fetch( 63 + `https://${instance}/api/v1/translate?sl=${encodeURIComponent(source)}&tl=${encodeURIComponent(target)}&text=${encodeURIComponent(text)}`, 64 + { 65 + priority: 'low', 66 + referrerPolicy: 'no-referrer', 67 + }, 68 + ); 69 + } 70 + return fetchPromise 42 71 .then((res) => { 43 72 if (!res.ok) throw new Error(res.statusText); 44 73 return res.json(); 45 74 }) 46 75 .then((res) => { 47 76 return { 48 - provider: 'lingva', 49 - content: res.translation, 50 - detectedSourceLanguage: res.info?.detectedSource, 51 - info: res.info, 77 + provider: 'translang', 78 + content: res.translated_text, 79 + detectedSourceLanguage: res.detected_language, 80 + pronunciation: res.pronunciation, 52 81 }; 53 82 }); 54 83 }; 55 84 return pRetry(fetchCall, { 56 85 retries: 3, 57 86 onFailedAttempt: (e) => { 58 - currentLingvaInstance = 59 - (currentLingvaInstance + 1) % LINGVA_INSTANCES.length; 87 + currentTranslangInstance = 88 + (currentTranslangInstance + 1) % TRANSLANG_INSTANCES.length; 60 89 console.log( 61 90 'Retrying translation with another instance', 62 - currentLingvaInstance, 91 + currentTranslangInstance, 63 92 ); 64 93 }, 65 94 }); 66 - // return masto.v1.statuses.$select(id).translate({ 67 - // lang: DEFAULT_LANG, 68 - // }); 69 95 } 70 - const TRANSLATED_MAX_AGE = 1000 * 60 * 60; // 1 hour 71 - const lingvaTranslate = pmem(_lingvaTranslate, { 96 + const translangTranslate = pmem(_translangTranslate, { 72 97 maxAge: TRANSLATED_MAX_AGE, 73 98 }); 74 - const throttledLingvaTranslate = pmem(throttle(lingvaTranslate), { 99 + const throttledTranslangTranslate = pmem(throttle(translangTranslate), { 75 100 // I know, this is double-layered memoization 76 101 maxAge: TRANSLATED_MAX_AGE, 77 102 }); ··· 99 124 const apiSourceLang = useRef('auto'); 100 125 101 126 if (!onTranslate) { 102 - // onTranslate = supportsBrowserTranslator 103 - // ? browserTranslate 104 - // : mini 105 - // ? throttledLingvaTranslate 106 - // : lingvaTranslate; 107 127 onTranslate = async (...args) => { 108 128 if (supportsBrowserTranslator) { 109 129 const result = await browserTranslate(...args); ··· 112 132 } 113 133 } 114 134 return mini 115 - ? await throttledLingvaTranslate(...args) 116 - : await lingvaTranslate(...args); 135 + ? await throttledTranslangTranslate(...args) 136 + : await translangTranslate(...args); 117 137 }; 118 138 } 119 139 ··· 127 147 const detectedLangText = localeCode2Text(detectedSourceLanguage); 128 148 setDetectedLang(detectedLangText); 129 149 } 130 - if (provider === 'lingva') { 131 - const pronunciation = props?.info?.pronunciation?.query; 150 + if (provider === 'translang') { 151 + const pronunciation = props?.pronunciation; 132 152 if (pronunciation) { 133 153 setPronunciationContent(pronunciation); 134 154 } ··· 235 255 code: l.code, 236 256 locale: l.code, 237 257 }); 238 - const showCommon = common !== native; 258 + const showCommon = native && common !== native; 239 259 return ( 240 260 <option value={l.code}> 241 261 {l.code === 'auto' 242 262 ? t`Auto (${detectedLang ?? '…'})` 243 263 : showCommon 244 264 ? `${native} - ${common}` 245 - : native} 265 + : common} 246 266 </option> 247 267 ); 248 268 })} ··· 280 300 ); 281 301 } 282 302 283 - export default LINGVA_INSTANCES?.length ? TranslationBlock : () => null; 303 + export default TRANSLANG_INSTANCES?.length ? TranslationBlock : () => null;
+46
src/data/listed-locales.json.orig
··· 1 + [ 2 + "ca-ES", 3 + <<<<<<< HEAD 4 + "es-ES", 5 + "fi-FI", 6 + "gl-ES", 7 + "it-IT", 8 + "pt-BR", 9 + "pt-PT", 10 + "zh-CN", 11 + "eo-UY", 12 + "ru-RU", 13 + "eu-ES", 14 + "lt-LT", 15 + "kab", 16 + "de-DE", 17 + "uk-UA", 18 + "fr-FR", 19 + "ko-KR", 20 + "cs-CZ", 21 + "nl-NL", 22 + "fa-IR", 23 + "pl-PL" 24 + ======= 25 + "cs-CZ", 26 + "de-DE", 27 + "eo-UY", 28 + "es-ES", 29 + "eu-ES", 30 + "fa-IR", 31 + "fi-FI", 32 + "fr-FR", 33 + "gl-ES", 34 + "it-IT", 35 + "kab", 36 + "ko-KR", 37 + "lt-LT", 38 + "nl-NL", 39 + "pl-PL", 40 + "pt-BR", 41 + "pt-PT", 42 + "ru-RU", 43 + "uk-UA", 44 + "zh-CN" 45 + >>>>>>> 8eeab341 (Move percentage threshold to build time) 46 + ]
+251
src/data/translang-languages-native.json
··· 1 + { 2 + "ab": "Аҧсшәа", 3 + "ace": "Acèh", 4 + "ach": "Lëbacoli", 5 + "aa": "Afár", 6 + "af": "Afrikaans", 7 + "sq": "Shqip", 8 + "alz": "Dhalur", 9 + "am": "አማርኛ", 10 + "ar": "العربية", 11 + "hy": "Հայերեն", 12 + "as": "অসমীয়া", 13 + "av": "Авар мацӀ", 14 + "awa": "अवधी", 15 + "ay": "Aymar aru", 16 + "az": "Azərbaycanca", 17 + "ban": "Basa Bali", 18 + "bal": "بلوچی", 19 + "bm": "Bamanankan", 20 + "bci": "Baoulé", 21 + "ba": "Башҡортса", 22 + "eu": "Euskara", 23 + "btx": "Batak Karo", 24 + "bts": "Batak Simalungun", 25 + "bbc": "Batak Toba", 26 + "be": "Беларуская", 27 + "bem": "Ichibemba", 28 + "bn": "বাংলা", 29 + "bew": "Betawi", 30 + "bho": "भोजपुरी", 31 + "bik": "Bikol", 32 + "bs": "Bosanski", 33 + "br": "Brezhoneg", 34 + "bg": "Български", 35 + "bua": "Буряад", 36 + "yue": "粵語", 37 + "ca": "Català", 38 + "ceb": "Cebuano", 39 + "ch": "Chamoru", 40 + "ce": "Нохчийн мотт", 41 + "ny": "Chichewa", 42 + "zh-CN": "简体中文", 43 + "zh-TW": "繁體中文", 44 + "chk": "Kapasen Chuuk", 45 + "cv": "Чӑвашла", 46 + "co": "Corsu", 47 + "crh": "Qırımtatarca (Кирилл)", 48 + "crh-Latn": "Qırımtatarca (Latin)", 49 + "hr": "Hrvatski", 50 + "cs": "Čeština", 51 + "da": "Dansk", 52 + "fa-AF": "درى", 53 + "dv": "ދިވެހި", 54 + "din": "Thuɔŋjäŋ", 55 + "doi": "डोगरी", 56 + "dov": "Dombe", 57 + "nl": "Nederlands", 58 + "dyu": "Jula", 59 + "dz": "རྫོང་ཁ", 60 + "en": "English", 61 + "eo": "Esperanto", 62 + "et": "Eesti", 63 + "ee": "Eʋegbe", 64 + "fo": "Føroyskt", 65 + "fj": "Vosa Vakaviti", 66 + "tl": "Tagalog", 67 + "fi": "Suomi", 68 + "fon": "Fon", 69 + "fr": "Français", 70 + "fr-CA": "Français (Canada)", 71 + "fy": "Frysk", 72 + "fur": "Furlan", 73 + "ff": "Fulfulde", 74 + "gaa": "Gã", 75 + "gl": "Galego", 76 + "ka": "ქართული", 77 + "de": "Deutsch", 78 + "el": "Ελληνικά", 79 + "gn": "Avañe'ẽ", 80 + "gu": "ગુજરાતી", 81 + "ht": "Kreyòl ayisyen", 82 + "cnh": "Hakha Chin", 83 + "ha": "Hausa", 84 + "haw": "ʻŌlelo Hawaiʻi", 85 + "iw": "עברית", 86 + "hil": "Hiligaynon", 87 + "hi": "हिन्दी", 88 + "hmn": "Hmoob", 89 + "hu": "Magyar", 90 + "hrx": "Hunsrik", 91 + "iba": "Iban", 92 + "is": "Íslenska", 93 + "ig": "Igbo", 94 + "ilo": "Iloko", 95 + "id": "Bahasa Indonesia", 96 + "iu-Latn": "Inuktitut (Latin)", 97 + "iu": "ᐃᓄᒃᑎᑐᑦ", 98 + "ga": "Gaeilge", 99 + "it": "Italiano", 100 + "jam": "Patwa", 101 + "ja": "日本語", 102 + "jw": "Basa Jawa", 103 + "kac": "Jinghpaw", 104 + "kl": "Kalaallisut", 105 + "kn": "ಕನ್ನಡ", 106 + "kr": "Kanuri", 107 + "pam": "Kapampangan", 108 + "kk": "Қазақша", 109 + "kha": "Khasi", 110 + "km": "ភាសាខ្មែរ", 111 + "cgg": "Rukiga", 112 + "kg": "Kikongo", 113 + "rw": "Kinyarwanda", 114 + "ktu": "Kituba", 115 + "trp": "Kokborok", 116 + "kv": "Коми кыв", 117 + "gom": "कोंकणी", 118 + "ko": "한국어", 119 + "kri": "Krio", 120 + "ku": "Kurdî (Kurmancî)", 121 + "ckb": "کوردی (سۆرانی)", 122 + "ky": "Кыргызча", 123 + "lo": "ລາວ", 124 + "ltg": "Latgaļu", 125 + "la": "Latina", 126 + "lv": "Latviešu", 127 + "lij": "Ligure", 128 + "li": "Limburgs", 129 + "ln": "Lingála", 130 + "lt": "Lietuvių", 131 + "lmo": "Lombard", 132 + "lg": "Luganda", 133 + "luo": "Dholuo", 134 + "lb": "Lëtzebuergesch", 135 + "mk": "Македонски", 136 + "mad": "Madhurâ", 137 + "mai": "मैथिली", 138 + "mak": "Makassar", 139 + "mg": "Malagasy", 140 + "ms": "Bahasa Melayu", 141 + "ms-Arab": "بهاس ملايو", 142 + "ml": "മലയാളം", 143 + "mt": "Malti", 144 + "mam": "Mam", 145 + "gv": "Gaelg", 146 + "mi": "Te Reo Māori", 147 + "mr": "मराठी", 148 + "mh": "Kajin M̧ajeļ", 149 + "mwr": "मारवाड़ी", 150 + "mfe": "Kreol Morisien", 151 + "chm": "Олык марий", 152 + "mni-Mtei": "ꯃꯤꯇꯩꯂꯣꯟ", 153 + "min": "Baso Minang", 154 + "lus": "Mizo ṭawng", 155 + "mn": "Монгол", 156 + "my": "မြန်မာစာ", 157 + "nhe": "Náhuatl", 158 + "ndc-ZW": "Ndau", 159 + "nr": "isiNdebele", 160 + "new": "नेपाल भाषा", 161 + "ne": "नेपाली", 162 + "bm-Nkoo": "ߒߞߏ", 163 + "no": "Norsk", 164 + "nus": "Thok Nath", 165 + "oc": "Occitan", 166 + "or": "ଓଡ଼ିଆ", 167 + "om": "Afaan Oromoo", 168 + "os": "Ирон æвзаг", 169 + "pag": "Pangasinan", 170 + "pap": "Papiamentu", 171 + "ps": "پښتو", 172 + "fa": "فارسی", 173 + "pl": "Polski", 174 + "pt": "Português (Brasil)", 175 + "pt-PT": "Português (Portugal)", 176 + "pa": "ਪੰਜਾਬੀ", 177 + "pa-Arab": "پنجابی", 178 + "qu": "Runa Simi", 179 + "kek": "Qʼeqchiʼ", 180 + "rom": "Romani čhib", 181 + "ro": "Română", 182 + "rn": "Ikirundi", 183 + "ru": "Русский", 184 + "se": "Davvisámegiella", 185 + "sm": "Gagana Samoa", 186 + "sg": "Sängö", 187 + "sa": "संस्कृतम्", 188 + "sat-Latn": "Santali (Latin)", 189 + "sat": "ᱥᱟᱱᱛᱟᱲᱤ", 190 + "gd": "Gàidhlig", 191 + "nso": "Sepedi", 192 + "sr": "Српски", 193 + "st": "Sesotho", 194 + "crs": "Kreol seselwa", 195 + "shn": "လိၵ်ႈတႆး", 196 + "sn": "chiShona", 197 + "scn": "Sicilianu", 198 + "szl": "Ślōnskŏ", 199 + "sd": "سنڌي", 200 + "si": "සිංහල", 201 + "sk": "Slovenčina", 202 + "sl": "Slovenščina", 203 + "so": "Soomaali", 204 + "es": "Español", 205 + "su": "Basa Sunda", 206 + "sus": "Susu", 207 + "sw": "Kiswahili", 208 + "ss": "siSwati", 209 + "sv": "Svenska", 210 + "ty": "Reo Tahiti", 211 + "tg": "Тоҷикӣ", 212 + "ber-Latn": "Tamazight (Latin)", 213 + "ber": "ⵜⴰⵎⴰⵣⵉⵖⵜ", 214 + "ta": "தமிழ்", 215 + "tt": "Татарча", 216 + "te": "తెలుగు", 217 + "tet": "Tetun", 218 + "th": "ไทย", 219 + "bo": "བོད་ཡིག", 220 + "ti": "ትግርኛ", 221 + "tiv": "Tiv", 222 + "tpi": "Tok Pisin", 223 + "to": "Lea fakatonga", 224 + "lua": "Tshiluba", 225 + "ts": "Xitsonga", 226 + "tn": "Setswana", 227 + "tcy": "ತುಳು", 228 + "tum": "chiTumbuka", 229 + "tr": "Türkçe", 230 + "tk": "Türkmençe", 231 + "tyv": "Тыва дыл", 232 + "ak": "Akankasa", 233 + "udm": "Удмурт кыл", 234 + "uk": "Українська", 235 + "ur": "اردو", 236 + "ug": "ئۇيغۇرچە", 237 + "uz": "Oʻzbekcha", 238 + "ve": "Tshivenḓa", 239 + "vec": "Vèneto", 240 + "vi": "Tiếng Việt", 241 + "war": "Winaray", 242 + "cy": "Cymraeg", 243 + "wo": "Wolof", 244 + "xh": "isiXhosa", 245 + "sah": "Саха тыла", 246 + "yi": "ייִדיש", 247 + "yo": "Yorùbá", 248 + "yua": "Màaya T'àan", 249 + "zap": "Didxazá", 250 + "zu": "isiZulu" 251 + }
+506
src/data/translang-languages.json
··· 1 + { 2 + "sl": { 3 + "auto": "Detect language", 4 + "ab": "Abkhaz", 5 + "ace": "Acehnese", 6 + "ach": "Acholi", 7 + "aa": "Afar", 8 + "af": "Afrikaans", 9 + "sq": "Albanian", 10 + "alz": "Alur", 11 + "am": "Amharic", 12 + "ar": "Arabic", 13 + "hy": "Armenian", 14 + "as": "Assamese", 15 + "av": "Avar", 16 + "awa": "Awadhi", 17 + "ay": "Aymara", 18 + "az": "Azerbaijani", 19 + "ban": "Balinese", 20 + "bal": "Baluchi", 21 + "bm": "Bambara", 22 + "bci": "Baoulé", 23 + "ba": "Bashkir", 24 + "eu": "Basque", 25 + "btx": "Batak Karo", 26 + "bts": "Batak Simalungun", 27 + "bbc": "Batak Toba", 28 + "be": "Belarusian", 29 + "bem": "Bemba", 30 + "bn": "Bengali", 31 + "bew": "Betawi", 32 + "bho": "Bhojpuri", 33 + "bik": "Bikol", 34 + "bs": "Bosnian", 35 + "br": "Breton", 36 + "bg": "Bulgarian", 37 + "bua": "Buryat", 38 + "yue": "Cantonese", 39 + "ca": "Catalan", 40 + "ceb": "Cebuano", 41 + "ch": "Chamorro", 42 + "ce": "Chechen", 43 + "ny": "Chichewa", 44 + "zh-CN": "Chinese (Simplified)", 45 + "zh-TW": "Chinese (Traditional)", 46 + "chk": "Chuukese", 47 + "cv": "Chuvash", 48 + "co": "Corsican", 49 + "crh": "Crimean Tatar (Cyrillic)", 50 + "crh-Latn": "Crimean Tatar (Latin)", 51 + "hr": "Croatian", 52 + "cs": "Czech", 53 + "da": "Danish", 54 + "fa-AF": "Dari", 55 + "dv": "Dhivehi", 56 + "din": "Dinka", 57 + "doi": "Dogri", 58 + "dov": "Dombe", 59 + "nl": "Dutch", 60 + "dyu": "Dyula", 61 + "dz": "Dzongkha", 62 + "en": "English", 63 + "eo": "Esperanto", 64 + "et": "Estonian", 65 + "ee": "Ewe", 66 + "fo": "Faroese", 67 + "fj": "Fijian", 68 + "tl": "Filipino", 69 + "fi": "Finnish", 70 + "fon": "Fon", 71 + "fr": "French", 72 + "fr-CA": "French (Canada)", 73 + "fy": "Frisian", 74 + "fur": "Friulian", 75 + "ff": "Fulani", 76 + "gaa": "Ga", 77 + "gl": "Galician", 78 + "ka": "Georgian", 79 + "de": "German", 80 + "el": "Greek", 81 + "gn": "Guarani", 82 + "gu": "Gujarati", 83 + "ht": "Haitian Creole", 84 + "cnh": "Hakha Chin", 85 + "ha": "Hausa", 86 + "haw": "Hawaiian", 87 + "iw": "Hebrew", 88 + "hil": "Hiligaynon", 89 + "hi": "Hindi", 90 + "hmn": "Hmong", 91 + "hu": "Hungarian", 92 + "hrx": "Hunsrik", 93 + "iba": "Iban", 94 + "is": "Icelandic", 95 + "ig": "Igbo", 96 + "ilo": "Ilocano", 97 + "id": "Indonesian", 98 + "iu-Latn": "Inuktut (Latin)", 99 + "iu": "Inuktut (Syllabics)", 100 + "ga": "Irish", 101 + "it": "Italian", 102 + "jam": "Jamaican Patois", 103 + "ja": "Japanese", 104 + "jw": "Javanese", 105 + "kac": "Jingpo", 106 + "kl": "Kalaallisut", 107 + "kn": "Kannada", 108 + "kr": "Kanuri", 109 + "pam": "Kapampangan", 110 + "kk": "Kazakh", 111 + "kha": "Khasi", 112 + "km": "Khmer", 113 + "cgg": "Kiga", 114 + "kg": "Kikongo", 115 + "rw": "Kinyarwanda", 116 + "ktu": "Kituba", 117 + "trp": "Kokborok", 118 + "kv": "Komi", 119 + "gom": "Konkani", 120 + "ko": "Korean", 121 + "kri": "Krio", 122 + "ku": "Kurdish (Kurmanji)", 123 + "ckb": "Kurdish (Sorani)", 124 + "ky": "Kyrgyz", 125 + "lo": "Lao", 126 + "ltg": "Latgalian", 127 + "la": "Latin", 128 + "lv": "Latvian", 129 + "lij": "Ligurian", 130 + "li": "Limburgish", 131 + "ln": "Lingala", 132 + "lt": "Lithuanian", 133 + "lmo": "Lombard", 134 + "lg": "Luganda", 135 + "luo": "Luo", 136 + "lb": "Luxembourgish", 137 + "mk": "Macedonian", 138 + "mad": "Madurese", 139 + "mai": "Maithili", 140 + "mak": "Makassar", 141 + "mg": "Malagasy", 142 + "ms": "Malay", 143 + "ms-Arab": "Malay (Jawi)", 144 + "ml": "Malayalam", 145 + "mt": "Maltese", 146 + "mam": "Mam", 147 + "gv": "Manx", 148 + "mi": "Maori", 149 + "mr": "Marathi", 150 + "mh": "Marshallese", 151 + "mwr": "Marwadi", 152 + "mfe": "Mauritian Creole", 153 + "chm": "Meadow Mari", 154 + "mni-Mtei": "Meiteilon (Manipuri)", 155 + "min": "Minang", 156 + "lus": "Mizo", 157 + "mn": "Mongolian", 158 + "my": "Myanmar (Burmese)", 159 + "nhe": "Nahuatl (Eastern Huasteca)", 160 + "ndc-ZW": "Ndau", 161 + "nr": "Ndebele (South)", 162 + "new": "Nepalbhasa (Newari)", 163 + "ne": "Nepali", 164 + "bm-Nkoo": "NKo", 165 + "no": "Norwegian", 166 + "nus": "Nuer", 167 + "oc": "Occitan", 168 + "or": "Odia (Oriya)", 169 + "om": "Oromo", 170 + "os": "Ossetian", 171 + "pag": "Pangasinan", 172 + "pap": "Papiamento", 173 + "ps": "Pashto", 174 + "fa": "Persian", 175 + "pl": "Polish", 176 + "pt": "Portuguese (Brazil)", 177 + "pt-PT": "Portuguese (Portugal)", 178 + "pa": "Punjabi (Gurmukhi)", 179 + "pa-Arab": "Punjabi (Shahmukhi)", 180 + "qu": "Quechua", 181 + "kek": "Qʼeqchiʼ", 182 + "rom": "Romani", 183 + "ro": "Romanian", 184 + "rn": "Rundi", 185 + "ru": "Russian", 186 + "se": "Sami (North)", 187 + "sm": "Samoan", 188 + "sg": "Sango", 189 + "sa": "Sanskrit", 190 + "sat-Latn": "Santali (Latin)", 191 + "sat": "Santali (Ol Chiki)", 192 + "gd": "Scots Gaelic", 193 + "nso": "Sepedi", 194 + "sr": "Serbian", 195 + "st": "Sesotho", 196 + "crs": "Seychellois Creole", 197 + "shn": "Shan", 198 + "sn": "Shona", 199 + "scn": "Sicilian", 200 + "szl": "Silesian", 201 + "sd": "Sindhi", 202 + "si": "Sinhala", 203 + "sk": "Slovak", 204 + "sl": "Slovenian", 205 + "so": "Somali", 206 + "es": "Spanish", 207 + "su": "Sundanese", 208 + "sus": "Susu", 209 + "sw": "Swahili", 210 + "ss": "Swati", 211 + "sv": "Swedish", 212 + "ty": "Tahitian", 213 + "tg": "Tajik", 214 + "ber-Latn": "Tamazight", 215 + "ber": "Tamazight (Tifinagh)", 216 + "ta": "Tamil", 217 + "tt": "Tatar", 218 + "te": "Telugu", 219 + "tet": "Tetum", 220 + "th": "Thai", 221 + "bo": "Tibetan", 222 + "ti": "Tigrinya", 223 + "tiv": "Tiv", 224 + "tpi": "Tok Pisin", 225 + "to": "Tongan", 226 + "lua": "Tshiluba", 227 + "ts": "Tsonga", 228 + "tn": "Tswana", 229 + "tcy": "Tulu", 230 + "tum": "Tumbuka", 231 + "tr": "Turkish", 232 + "tk": "Turkmen", 233 + "tyv": "Tuvan", 234 + "ak": "Twi", 235 + "udm": "Udmurt", 236 + "uk": "Ukrainian", 237 + "ur": "Urdu", 238 + "ug": "Uyghur", 239 + "uz": "Uzbek", 240 + "ve": "Venda", 241 + "vec": "Venetian", 242 + "vi": "Vietnamese", 243 + "war": "Waray", 244 + "cy": "Welsh", 245 + "wo": "Wolof", 246 + "xh": "Xhosa", 247 + "sah": "Yakut", 248 + "yi": "Yiddish", 249 + "yo": "Yoruba", 250 + "yua": "Yucatec Maya", 251 + "zap": "Zapotec", 252 + "zu": "Zulu" 253 + }, 254 + "tl": { 255 + "ab": "Abkhaz", 256 + "ace": "Acehnese", 257 + "ach": "Acholi", 258 + "aa": "Afar", 259 + "af": "Afrikaans", 260 + "sq": "Albanian", 261 + "alz": "Alur", 262 + "am": "Amharic", 263 + "ar": "Arabic", 264 + "hy": "Armenian", 265 + "as": "Assamese", 266 + "av": "Avar", 267 + "awa": "Awadhi", 268 + "ay": "Aymara", 269 + "az": "Azerbaijani", 270 + "ban": "Balinese", 271 + "bal": "Baluchi", 272 + "bm": "Bambara", 273 + "bci": "Baoulé", 274 + "ba": "Bashkir", 275 + "eu": "Basque", 276 + "btx": "Batak Karo", 277 + "bts": "Batak Simalungun", 278 + "bbc": "Batak Toba", 279 + "be": "Belarusian", 280 + "bem": "Bemba", 281 + "bn": "Bengali", 282 + "bew": "Betawi", 283 + "bho": "Bhojpuri", 284 + "bik": "Bikol", 285 + "bs": "Bosnian", 286 + "br": "Breton", 287 + "bg": "Bulgarian", 288 + "bua": "Buryat", 289 + "yue": "Cantonese", 290 + "ca": "Catalan", 291 + "ceb": "Cebuano", 292 + "ch": "Chamorro", 293 + "ce": "Chechen", 294 + "ny": "Chichewa", 295 + "zh-CN": "Chinese (Simplified)", 296 + "zh-TW": "Chinese (Traditional)", 297 + "chk": "Chuukese", 298 + "cv": "Chuvash", 299 + "co": "Corsican", 300 + "crh": "Crimean Tatar (Cyrillic)", 301 + "crh-Latn": "Crimean Tatar (Latin)", 302 + "hr": "Croatian", 303 + "cs": "Czech", 304 + "da": "Danish", 305 + "fa-AF": "Dari", 306 + "dv": "Dhivehi", 307 + "din": "Dinka", 308 + "doi": "Dogri", 309 + "dov": "Dombe", 310 + "nl": "Dutch", 311 + "dyu": "Dyula", 312 + "dz": "Dzongkha", 313 + "en": "English", 314 + "eo": "Esperanto", 315 + "et": "Estonian", 316 + "ee": "Ewe", 317 + "fo": "Faroese", 318 + "fj": "Fijian", 319 + "tl": "Filipino", 320 + "fi": "Finnish", 321 + "fon": "Fon", 322 + "fr": "French", 323 + "fr-CA": "French (Canada)", 324 + "fy": "Frisian", 325 + "fur": "Friulian", 326 + "ff": "Fulani", 327 + "gaa": "Ga", 328 + "gl": "Galician", 329 + "ka": "Georgian", 330 + "de": "German", 331 + "el": "Greek", 332 + "gn": "Guarani", 333 + "gu": "Gujarati", 334 + "ht": "Haitian Creole", 335 + "cnh": "Hakha Chin", 336 + "ha": "Hausa", 337 + "haw": "Hawaiian", 338 + "iw": "Hebrew", 339 + "hil": "Hiligaynon", 340 + "hi": "Hindi", 341 + "hmn": "Hmong", 342 + "hu": "Hungarian", 343 + "hrx": "Hunsrik", 344 + "iba": "Iban", 345 + "is": "Icelandic", 346 + "ig": "Igbo", 347 + "ilo": "Ilocano", 348 + "id": "Indonesian", 349 + "iu-Latn": "Inuktut (Latin)", 350 + "iu": "Inuktut (Syllabics)", 351 + "ga": "Irish", 352 + "it": "Italian", 353 + "jam": "Jamaican Patois", 354 + "ja": "Japanese", 355 + "jw": "Javanese", 356 + "kac": "Jingpo", 357 + "kl": "Kalaallisut", 358 + "kn": "Kannada", 359 + "kr": "Kanuri", 360 + "pam": "Kapampangan", 361 + "kk": "Kazakh", 362 + "kha": "Khasi", 363 + "km": "Khmer", 364 + "cgg": "Kiga", 365 + "kg": "Kikongo", 366 + "rw": "Kinyarwanda", 367 + "ktu": "Kituba", 368 + "trp": "Kokborok", 369 + "kv": "Komi", 370 + "gom": "Konkani", 371 + "ko": "Korean", 372 + "kri": "Krio", 373 + "ku": "Kurdish (Kurmanji)", 374 + "ckb": "Kurdish (Sorani)", 375 + "ky": "Kyrgyz", 376 + "lo": "Lao", 377 + "ltg": "Latgalian", 378 + "la": "Latin", 379 + "lv": "Latvian", 380 + "lij": "Ligurian", 381 + "li": "Limburgish", 382 + "ln": "Lingala", 383 + "lt": "Lithuanian", 384 + "lmo": "Lombard", 385 + "lg": "Luganda", 386 + "luo": "Luo", 387 + "lb": "Luxembourgish", 388 + "mk": "Macedonian", 389 + "mad": "Madurese", 390 + "mai": "Maithili", 391 + "mak": "Makassar", 392 + "mg": "Malagasy", 393 + "ms": "Malay", 394 + "ms-Arab": "Malay (Jawi)", 395 + "ml": "Malayalam", 396 + "mt": "Maltese", 397 + "mam": "Mam", 398 + "gv": "Manx", 399 + "mi": "Maori", 400 + "mr": "Marathi", 401 + "mh": "Marshallese", 402 + "mwr": "Marwadi", 403 + "mfe": "Mauritian Creole", 404 + "chm": "Meadow Mari", 405 + "mni-Mtei": "Meiteilon (Manipuri)", 406 + "min": "Minang", 407 + "lus": "Mizo", 408 + "mn": "Mongolian", 409 + "my": "Myanmar (Burmese)", 410 + "nhe": "Nahuatl (Eastern Huasteca)", 411 + "ndc-ZW": "Ndau", 412 + "nr": "Ndebele (South)", 413 + "new": "Nepalbhasa (Newari)", 414 + "ne": "Nepali", 415 + "bm-Nkoo": "NKo", 416 + "no": "Norwegian", 417 + "nus": "Nuer", 418 + "oc": "Occitan", 419 + "or": "Odia (Oriya)", 420 + "om": "Oromo", 421 + "os": "Ossetian", 422 + "pag": "Pangasinan", 423 + "pap": "Papiamento", 424 + "ps": "Pashto", 425 + "fa": "Persian", 426 + "pl": "Polish", 427 + "pt": "Portuguese (Brazil)", 428 + "pt-PT": "Portuguese (Portugal)", 429 + "pa": "Punjabi (Gurmukhi)", 430 + "pa-Arab": "Punjabi (Shahmukhi)", 431 + "qu": "Quechua", 432 + "kek": "Qʼeqchiʼ", 433 + "rom": "Romani", 434 + "ro": "Romanian", 435 + "rn": "Rundi", 436 + "ru": "Russian", 437 + "se": "Sami (North)", 438 + "sm": "Samoan", 439 + "sg": "Sango", 440 + "sa": "Sanskrit", 441 + "sat-Latn": "Santali (Latin)", 442 + "sat": "Santali (Ol Chiki)", 443 + "gd": "Scots Gaelic", 444 + "nso": "Sepedi", 445 + "sr": "Serbian", 446 + "st": "Sesotho", 447 + "crs": "Seychellois Creole", 448 + "shn": "Shan", 449 + "sn": "Shona", 450 + "scn": "Sicilian", 451 + "szl": "Silesian", 452 + "sd": "Sindhi", 453 + "si": "Sinhala", 454 + "sk": "Slovak", 455 + "sl": "Slovenian", 456 + "so": "Somali", 457 + "es": "Spanish", 458 + "su": "Sundanese", 459 + "sus": "Susu", 460 + "sw": "Swahili", 461 + "ss": "Swati", 462 + "sv": "Swedish", 463 + "ty": "Tahitian", 464 + "tg": "Tajik", 465 + "ber-Latn": "Tamazight", 466 + "ber": "Tamazight (Tifinagh)", 467 + "ta": "Tamil", 468 + "tt": "Tatar", 469 + "te": "Telugu", 470 + "tet": "Tetum", 471 + "th": "Thai", 472 + "bo": "Tibetan", 473 + "ti": "Tigrinya", 474 + "tiv": "Tiv", 475 + "tpi": "Tok Pisin", 476 + "to": "Tongan", 477 + "lua": "Tshiluba", 478 + "ts": "Tsonga", 479 + "tn": "Tswana", 480 + "tcy": "Tulu", 481 + "tum": "Tumbuka", 482 + "tr": "Turkish", 483 + "tk": "Turkmen", 484 + "tyv": "Tuvan", 485 + "ak": "Twi", 486 + "udm": "Udmurt", 487 + "uk": "Ukrainian", 488 + "ur": "Urdu", 489 + "ug": "Uyghur", 490 + "uz": "Uzbek", 491 + "ve": "Venda", 492 + "vec": "Venetian", 493 + "vi": "Vietnamese", 494 + "war": "Waray", 495 + "cy": "Welsh", 496 + "wo": "Wolof", 497 + "xh": "Xhosa", 498 + "sah": "Yakut", 499 + "yi": "Yiddish", 500 + "yo": "Yoruba", 501 + "yua": "Yucatec Maya", 502 + "zap": "Zapotec", 503 + "zu": "Zulu" 504 + }, 505 + "al": {} 506 + }
+213 -213
src/locales/en.po
··· 34 34 35 35 #: src/components/account-block.jsx:169 36 36 #: src/components/account-info.jsx:695 37 - #: src/components/status.jsx:571 37 + #: src/components/status.jsx:588 38 38 msgid "Group" 39 39 msgstr "" 40 40 ··· 111 111 #: src/components/compose.jsx:2772 112 112 #: src/components/media-alt-modal.jsx:46 113 113 #: src/components/media-modal.jsx:358 114 - #: src/components/status.jsx:1809 115 114 #: src/components/status.jsx:1826 116 - #: src/components/status.jsx:1951 117 - #: src/components/status.jsx:2569 118 - #: src/components/status.jsx:2572 115 + #: src/components/status.jsx:1843 116 + #: src/components/status.jsx:1968 117 + #: src/components/status.jsx:2588 118 + #: src/components/status.jsx:2591 119 119 #: src/pages/account-statuses.jsx:523 120 120 #: src/pages/accounts.jsx:110 121 121 #: src/pages/hashtag.jsx:200 ··· 203 203 msgstr "" 204 204 205 205 #: src/components/account-info.jsx:926 206 - #: src/components/status.jsx:2353 206 + #: src/components/status.jsx:2372 207 207 #: src/pages/catchup.jsx:72 208 208 #: src/pages/catchup.jsx:1447 209 209 #: src/pages/catchup.jsx:2062 ··· 216 216 #: src/pages/catchup.jsx:73 217 217 #: src/pages/catchup.jsx:1449 218 218 #: src/pages/catchup.jsx:2074 219 - #: src/pages/settings.jsx:1154 219 + #: src/pages/settings.jsx:1159 220 220 msgid "Boosts" 221 221 msgstr "" 222 222 ··· 300 300 msgstr "" 301 301 302 302 #: src/components/account-info.jsx:1384 303 - #: src/components/status.jsx:1236 303 + #: src/components/status.jsx:1253 304 304 msgid "Link copied" 305 305 msgstr "" 306 306 307 307 #: src/components/account-info.jsx:1387 308 - #: src/components/status.jsx:1239 308 + #: src/components/status.jsx:1256 309 309 msgid "Unable to copy link" 310 310 msgstr "" 311 311 312 312 #: src/components/account-info.jsx:1393 313 313 #: src/components/shortcuts-settings.jsx:1059 314 - #: src/components/status.jsx:1245 315 - #: src/components/status.jsx:3340 314 + #: src/components/status.jsx:1262 315 + #: src/components/status.jsx:3359 316 316 msgid "Copy" 317 317 msgstr "" 318 318 319 319 #: src/components/account-info.jsx:1408 320 320 #: src/components/shortcuts-settings.jsx:1077 321 - #: src/components/status.jsx:1261 321 + #: src/components/status.jsx:1278 322 322 msgid "Sharing doesn't seem to work." 323 323 msgstr "" 324 324 325 325 #: src/components/account-info.jsx:1414 326 - #: src/components/status.jsx:1267 326 + #: src/components/status.jsx:1284 327 327 msgid "Share…" 328 328 msgstr "" 329 329 ··· 437 437 #: src/components/shortcuts-settings.jsx:230 438 438 #: src/components/shortcuts-settings.jsx:583 439 439 #: src/components/shortcuts-settings.jsx:783 440 - #: src/components/status.jsx:3064 441 - #: src/components/status.jsx:3304 442 - #: src/components/status.jsx:3813 440 + #: src/components/status.jsx:3083 441 + #: src/components/status.jsx:3323 442 + #: src/components/status.jsx:3832 443 443 #: src/pages/accounts.jsx:37 444 444 #: src/pages/catchup.jsx:1583 445 445 #: src/pages/filters.jsx:225 446 446 #: src/pages/list.jsx:276 447 447 #: src/pages/notifications.jsx:934 448 448 #: src/pages/scheduled-posts.jsx:257 449 - #: src/pages/settings.jsx:79 449 + #: src/pages/settings.jsx:87 450 450 #: src/pages/status.jsx:1319 451 451 msgid "Close" 452 452 msgstr "" ··· 680 680 msgstr "Attachment #{i} failed" 681 681 682 682 #: src/components/compose.jsx:1211 683 - #: src/components/status.jsx:2139 683 + #: src/components/status.jsx:2156 684 684 #: src/components/timeline.jsx:1017 685 685 msgid "Content warning" 686 686 msgstr "" ··· 691 691 692 692 #: src/components/compose.jsx:1263 693 693 #: src/components/status.jsx:96 694 - #: src/pages/settings.jsx:307 694 + #: src/pages/settings.jsx:315 695 695 msgid "Public" 696 696 msgstr "" 697 697 ··· 704 704 705 705 #: src/components/compose.jsx:1272 706 706 #: src/components/status.jsx:98 707 - #: src/pages/settings.jsx:310 707 + #: src/pages/settings.jsx:318 708 708 msgid "Unlisted" 709 709 msgstr "" 710 710 711 711 #: src/components/compose.jsx:1275 712 712 #: src/components/status.jsx:99 713 - #: src/pages/settings.jsx:313 713 + #: src/pages/settings.jsx:321 714 714 msgid "Followers only" 715 715 msgstr "" 716 716 717 717 #: src/components/compose.jsx:1278 718 718 #: src/components/status.jsx:100 719 - #: src/components/status.jsx:2015 719 + #: src/components/status.jsx:2032 720 720 msgid "Private mention" 721 721 msgstr "" 722 722 ··· 753 753 754 754 #: src/components/compose.jsx:1664 755 755 #: src/components/keyboard-shortcuts-help.jsx:155 756 - #: src/components/status.jsx:1008 757 - #: src/components/status.jsx:1789 758 - #: src/components/status.jsx:1790 759 - #: src/components/status.jsx:2473 756 + #: src/components/status.jsx:1025 757 + #: src/components/status.jsx:1806 758 + #: src/components/status.jsx:1807 759 + #: src/components/status.jsx:2492 760 760 msgid "Reply" 761 761 msgstr "" 762 762 ··· 960 960 msgstr "" 961 961 962 962 #: src/components/drafts.jsx:64 963 - #: src/pages/settings.jsx:696 963 + #: src/pages/settings.jsx:701 964 964 msgid "Unsent drafts" 965 965 msgstr "" 966 966 ··· 978 978 979 979 #: src/components/drafts.jsx:128 980 980 #: src/components/list-add-edit.jsx:186 981 - #: src/components/status.jsx:1411 981 + #: src/components/status.jsx:1428 982 982 #: src/pages/filters.jsx:603 983 983 #: src/pages/scheduled-posts.jsx:367 984 984 msgid "Delete…" ··· 1187 1187 msgstr "" 1188 1188 1189 1189 #: src/components/keyboard-shortcuts-help.jsx:176 1190 - #: src/components/status.jsx:1016 1191 - #: src/components/status.jsx:2500 1192 - #: src/components/status.jsx:2523 1193 - #: src/components/status.jsx:2524 1190 + #: src/components/status.jsx:1033 1191 + #: src/components/status.jsx:2519 1192 + #: src/components/status.jsx:2542 1193 + #: src/components/status.jsx:2543 1194 1194 msgid "Boost" 1195 1195 msgstr "" 1196 1196 ··· 1199 1199 msgstr "" 1200 1200 1201 1201 #: src/components/keyboard-shortcuts-help.jsx:184 1202 - #: src/components/status.jsx:1079 1203 - #: src/components/status.jsx:2548 1204 - #: src/components/status.jsx:2549 1202 + #: src/components/status.jsx:1096 1203 + #: src/components/status.jsx:2567 1204 + #: src/components/status.jsx:2568 1205 1205 msgid "Bookmark" 1206 1206 msgstr "" 1207 1207 ··· 1260 1260 msgstr "" 1261 1261 1262 1262 #: src/components/media-alt-modal.jsx:58 1263 - #: src/components/status.jsx:1122 1264 - #: src/components/status.jsx:1131 1265 - #: src/components/translation-block.jsx:215 1263 + #: src/components/status.jsx:1139 1264 + #: src/components/status.jsx:1148 1265 + #: src/components/translation-block.jsx:235 1266 1266 msgid "Translate" 1267 1267 msgstr "" 1268 1268 1269 1269 #: src/components/media-alt-modal.jsx:69 1270 - #: src/components/status.jsx:1150 1270 + #: src/components/status.jsx:1167 1271 1271 msgid "Speak" 1272 1272 msgstr "" 1273 1273 ··· 1304 1304 msgstr "" 1305 1305 1306 1306 #: src/components/media-post.jsx:134 1307 - #: src/components/status.jsx:3643 1308 - #: src/components/status.jsx:3739 1309 - #: src/components/status.jsx:3817 1307 + #: src/components/status.jsx:3662 1308 + #: src/components/status.jsx:3758 1309 + #: src/components/status.jsx:3836 1310 1310 #: src/components/timeline.jsx:1006 1311 1311 #: src/pages/catchup.jsx:76 1312 1312 #: src/pages/catchup.jsx:1881 ··· 1368 1368 #: src/pages/home.jsx:228 1369 1369 #: src/pages/mentions.jsx:21 1370 1370 #: src/pages/mentions.jsx:168 1371 - #: src/pages/settings.jsx:1146 1371 + #: src/pages/settings.jsx:1151 1372 1372 #: src/pages/trending.jsx:379 1373 1373 msgid "Mentions" 1374 1374 msgstr "" ··· 1407 1407 #: src/pages/catchup.jsx:2068 1408 1408 #: src/pages/favourites.jsx:12 1409 1409 #: src/pages/favourites.jsx:24 1410 - #: src/pages/settings.jsx:1150 1410 + #: src/pages/settings.jsx:1155 1411 1411 msgid "Likes" 1412 1412 msgstr "" 1413 1413 ··· 1618 1618 msgstr "" 1619 1619 1620 1620 #: src/components/notification.jsx:441 1621 - #: src/components/status.jsx:1093 1622 - #: src/components/status.jsx:1103 1621 + #: src/components/status.jsx:1110 1622 + #: src/components/status.jsx:1120 1623 1623 msgid "Boosted/Liked by…" 1624 1624 msgstr "" 1625 1625 ··· 1645 1645 msgstr "View #Wrapstodon" 1646 1646 1647 1647 #: src/components/notification.jsx:772 1648 - #: src/components/status.jsx:299 1648 + #: src/components/status.jsx:316 1649 1649 msgid "Read more →" 1650 1650 msgstr "" 1651 1651 ··· 1947 1947 msgstr "" 1948 1948 1949 1949 #: src/components/shortcuts-settings.jsx:379 1950 - #: src/components/status.jsx:1373 1950 + #: src/components/status.jsx:1390 1951 1951 #: src/pages/list.jsx:171 1952 1952 msgid "Edit" 1953 1953 msgstr "" ··· 2146 2146 msgid "Import/export settings from/to instance server (Very experimental)" 2147 2147 msgstr "" 2148 2148 2149 - #: src/components/status.jsx:595 2149 + #: src/components/status.jsx:612 2150 2150 msgid "<0/> <1>boosted</1>" 2151 2151 msgstr "" 2152 2152 2153 - #: src/components/status.jsx:694 2153 + #: src/components/status.jsx:711 2154 2154 msgid "Sorry, your current logged-in instance can't interact with this post from another instance." 2155 2155 msgstr "" 2156 2156 2157 2157 #. placeholder {0}: username || acct 2158 - #: src/components/status.jsx:847 2158 + #: src/components/status.jsx:864 2159 2159 msgid "Unliked @{0}'s post" 2160 2160 msgstr "" 2161 2161 2162 2162 #. placeholder {0}: username || acct 2163 - #: src/components/status.jsx:848 2163 + #: src/components/status.jsx:865 2164 2164 msgid "Liked @{0}'s post" 2165 2165 msgstr "Liked @{0}'s post" 2166 2166 2167 2167 #. placeholder {0}: username || acct 2168 - #: src/components/status.jsx:887 2168 + #: src/components/status.jsx:904 2169 2169 msgid "Unbookmarked @{0}'s post" 2170 2170 msgstr "Unbookmarked @{0}'s post" 2171 2171 2172 2172 #. placeholder {0}: username || acct 2173 - #: src/components/status.jsx:888 2173 + #: src/components/status.jsx:905 2174 2174 msgid "Bookmarked @{0}'s post" 2175 2175 msgstr "Bookmarked @{0}'s post" 2176 2176 2177 - #: src/components/status.jsx:985 2177 + #: src/components/status.jsx:1002 2178 2178 msgid "Some media have no descriptions." 2179 2179 msgstr "" 2180 2180 2181 2181 #. placeholder {0}: rtf.format(-statusMonthsAgo, 'month') 2182 - #: src/components/status.jsx:992 2182 + #: src/components/status.jsx:1009 2183 2183 msgid "Old post (<0>{0}</0>)" 2184 2184 msgstr "" 2185 2185 2186 - #: src/components/status.jsx:1016 2187 - #: src/components/status.jsx:1056 2188 - #: src/components/status.jsx:2500 2189 - #: src/components/status.jsx:2523 2186 + #: src/components/status.jsx:1033 2187 + #: src/components/status.jsx:1073 2188 + #: src/components/status.jsx:2519 2189 + #: src/components/status.jsx:2542 2190 2190 msgid "Unboost" 2191 2191 msgstr "" 2192 2192 2193 - #: src/components/status.jsx:1032 2194 - #: src/components/status.jsx:2515 2193 + #: src/components/status.jsx:1049 2194 + #: src/components/status.jsx:2534 2195 2195 msgid "Quote" 2196 2196 msgstr "" 2197 2197 2198 2198 #. placeholder {0}: username || acct 2199 - #: src/components/status.jsx:1044 2200 - #: src/components/status.jsx:1507 2199 + #: src/components/status.jsx:1061 2200 + #: src/components/status.jsx:1524 2201 2201 msgid "Unboosted @{0}'s post" 2202 2202 msgstr "Unboosted @{0}'s post" 2203 2203 2204 2204 #. placeholder {0}: username || acct 2205 - #: src/components/status.jsx:1045 2206 - #: src/components/status.jsx:1508 2205 + #: src/components/status.jsx:1062 2206 + #: src/components/status.jsx:1525 2207 2207 msgid "Boosted @{0}'s post" 2208 2208 msgstr "Boosted @{0}'s post" 2209 2209 2210 - #: src/components/status.jsx:1057 2210 + #: src/components/status.jsx:1074 2211 2211 msgid "Boost…" 2212 2212 msgstr "" 2213 2213 2214 - #: src/components/status.jsx:1069 2215 - #: src/components/status.jsx:1799 2216 - #: src/components/status.jsx:2536 2214 + #: src/components/status.jsx:1086 2215 + #: src/components/status.jsx:1816 2216 + #: src/components/status.jsx:2555 2217 2217 msgid "Unlike" 2218 2218 msgstr "" 2219 2219 2220 - #: src/components/status.jsx:1070 2221 - #: src/components/status.jsx:1799 2222 - #: src/components/status.jsx:1800 2223 - #: src/components/status.jsx:2536 2224 - #: src/components/status.jsx:2537 2220 + #: src/components/status.jsx:1087 2221 + #: src/components/status.jsx:1816 2222 + #: src/components/status.jsx:1817 2223 + #: src/components/status.jsx:2555 2224 + #: src/components/status.jsx:2556 2225 2225 msgid "Like" 2226 2226 msgstr "" 2227 2227 2228 - #: src/components/status.jsx:1079 2229 - #: src/components/status.jsx:2548 2228 + #: src/components/status.jsx:1096 2229 + #: src/components/status.jsx:2567 2230 2230 msgid "Unbookmark" 2231 2231 msgstr "" 2232 2232 2233 - #: src/components/status.jsx:1162 2233 + #: src/components/status.jsx:1179 2234 2234 msgid "Post text copied" 2235 2235 msgstr "Post text copied" 2236 2236 2237 - #: src/components/status.jsx:1165 2237 + #: src/components/status.jsx:1182 2238 2238 msgid "Unable to copy post text" 2239 2239 msgstr "Unable to copy post text" 2240 2240 2241 - #: src/components/status.jsx:1171 2241 + #: src/components/status.jsx:1188 2242 2242 msgid "Copy post text" 2243 2243 msgstr "Copy post text" 2244 2244 2245 2245 #. placeholder {0}: username || acct 2246 - #: src/components/status.jsx:1189 2246 + #: src/components/status.jsx:1206 2247 2247 msgid "View post by <0>@{0}</0>" 2248 2248 msgstr "" 2249 2249 2250 - #: src/components/status.jsx:1210 2250 + #: src/components/status.jsx:1227 2251 2251 msgid "Show Edit History" 2252 2252 msgstr "" 2253 2253 2254 - #: src/components/status.jsx:1213 2254 + #: src/components/status.jsx:1230 2255 2255 msgid "Edited: {editedDateText}" 2256 2256 msgstr "" 2257 2257 2258 - #: src/components/status.jsx:1280 2259 - #: src/components/status.jsx:3309 2258 + #: src/components/status.jsx:1297 2259 + #: src/components/status.jsx:3328 2260 2260 msgid "Embed post" 2261 2261 msgstr "" 2262 2262 2263 - #: src/components/status.jsx:1294 2263 + #: src/components/status.jsx:1311 2264 2264 msgid "Conversation unmuted" 2265 2265 msgstr "" 2266 2266 2267 - #: src/components/status.jsx:1294 2267 + #: src/components/status.jsx:1311 2268 2268 msgid "Conversation muted" 2269 2269 msgstr "" 2270 2270 2271 - #: src/components/status.jsx:1300 2271 + #: src/components/status.jsx:1317 2272 2272 msgid "Unable to unmute conversation" 2273 2273 msgstr "" 2274 2274 2275 - #: src/components/status.jsx:1301 2275 + #: src/components/status.jsx:1318 2276 2276 msgid "Unable to mute conversation" 2277 2277 msgstr "" 2278 2278 2279 - #: src/components/status.jsx:1310 2279 + #: src/components/status.jsx:1327 2280 2280 msgid "Unmute conversation" 2281 2281 msgstr "" 2282 2282 2283 - #: src/components/status.jsx:1317 2283 + #: src/components/status.jsx:1334 2284 2284 msgid "Mute conversation" 2285 2285 msgstr "" 2286 2286 2287 - #: src/components/status.jsx:1333 2287 + #: src/components/status.jsx:1350 2288 2288 msgid "Post unpinned from profile" 2289 2289 msgstr "" 2290 2290 2291 - #: src/components/status.jsx:1334 2291 + #: src/components/status.jsx:1351 2292 2292 msgid "Post pinned to profile" 2293 2293 msgstr "" 2294 2294 2295 - #: src/components/status.jsx:1339 2295 + #: src/components/status.jsx:1356 2296 2296 msgid "Unable to unpin post" 2297 2297 msgstr "" 2298 2298 2299 - #: src/components/status.jsx:1339 2299 + #: src/components/status.jsx:1356 2300 2300 msgid "Unable to pin post" 2301 2301 msgstr "" 2302 2302 2303 - #: src/components/status.jsx:1348 2303 + #: src/components/status.jsx:1365 2304 2304 msgid "Unpin from profile" 2305 2305 msgstr "" 2306 2306 2307 - #: src/components/status.jsx:1355 2307 + #: src/components/status.jsx:1372 2308 2308 msgid "Pin to profile" 2309 2309 msgstr "" 2310 2310 2311 - #: src/components/status.jsx:1384 2311 + #: src/components/status.jsx:1401 2312 2312 msgid "Delete this post?" 2313 2313 msgstr "" 2314 2314 2315 - #: src/components/status.jsx:1400 2315 + #: src/components/status.jsx:1417 2316 2316 msgid "Post deleted" 2317 2317 msgstr "" 2318 2318 2319 - #: src/components/status.jsx:1403 2319 + #: src/components/status.jsx:1420 2320 2320 msgid "Unable to delete post" 2321 2321 msgstr "" 2322 2322 2323 - #: src/components/status.jsx:1431 2323 + #: src/components/status.jsx:1448 2324 2324 msgid "Report post…" 2325 2325 msgstr "" 2326 2326 2327 - #: src/components/status.jsx:1800 2328 - #: src/components/status.jsx:1836 2329 - #: src/components/status.jsx:2537 2327 + #: src/components/status.jsx:1817 2328 + #: src/components/status.jsx:1853 2329 + #: src/components/status.jsx:2556 2330 2330 msgid "Liked" 2331 2331 msgstr "" 2332 2332 2333 - #: src/components/status.jsx:1833 2334 - #: src/components/status.jsx:2524 2333 + #: src/components/status.jsx:1850 2334 + #: src/components/status.jsx:2543 2335 2335 msgid "Boosted" 2336 2336 msgstr "" 2337 2337 2338 - #: src/components/status.jsx:1843 2339 - #: src/components/status.jsx:2549 2338 + #: src/components/status.jsx:1860 2339 + #: src/components/status.jsx:2568 2340 2340 msgid "Bookmarked" 2341 2341 msgstr "" 2342 2342 2343 - #: src/components/status.jsx:1847 2343 + #: src/components/status.jsx:1864 2344 2344 msgid "Pinned" 2345 2345 msgstr "" 2346 2346 2347 - #: src/components/status.jsx:1893 2348 - #: src/components/status.jsx:2361 2347 + #: src/components/status.jsx:1910 2348 + #: src/components/status.jsx:2380 2349 2349 msgid "Deleted" 2350 2350 msgstr "" 2351 2351 2352 - #: src/components/status.jsx:1934 2352 + #: src/components/status.jsx:1951 2353 2353 msgid "{repliesCount, plural, one {# reply} other {# replies}}" 2354 2354 msgstr "" 2355 2355 2356 2356 #. placeholder {0}: snapStates.statusThreadNumber[sKey] ? ` ${snapStates.statusThreadNumber[sKey]}/X` : '' 2357 - #: src/components/status.jsx:2024 2357 + #: src/components/status.jsx:2041 2358 2358 msgid "Thread{0}" 2359 2359 msgstr "" 2360 2360 2361 - #: src/components/status.jsx:2102 2362 - #: src/components/status.jsx:2164 2363 - #: src/components/status.jsx:2257 2361 + #: src/components/status.jsx:2119 2362 + #: src/components/status.jsx:2181 2363 + #: src/components/status.jsx:2276 2364 2364 msgid "Show less" 2365 2365 msgstr "" 2366 2366 2367 - #: src/components/status.jsx:2102 2368 - #: src/components/status.jsx:2164 2367 + #: src/components/status.jsx:2119 2368 + #: src/components/status.jsx:2181 2369 2369 msgid "Show content" 2370 2370 msgstr "" 2371 2371 2372 2372 #. placeholder {0}: filterInfo.titlesStr 2373 2373 #. placeholder {0}: filterInfo?.titlesStr 2374 - #: src/components/status.jsx:2253 2374 + #: src/components/status.jsx:2272 2375 2375 #: src/pages/catchup.jsx:1880 2376 2376 msgid "Filtered: {0}" 2377 2377 msgstr "Filtered: {0}" 2378 2378 2379 - #: src/components/status.jsx:2257 2379 + #: src/components/status.jsx:2276 2380 2380 msgid "Show media" 2381 2381 msgstr "" 2382 2382 2383 - #: src/components/status.jsx:2397 2383 + #: src/components/status.jsx:2416 2384 2384 msgid "Edited" 2385 2385 msgstr "" 2386 2386 2387 - #: src/components/status.jsx:2474 2387 + #: src/components/status.jsx:2493 2388 2388 msgid "Comments" 2389 2389 msgstr "" 2390 2390 2391 2391 #. More from [Author] 2392 - #: src/components/status.jsx:2767 2392 + #: src/components/status.jsx:2786 2393 2393 msgid "More from <0/>" 2394 2394 msgstr "More from <0/>" 2395 2395 2396 - #: src/components/status.jsx:3069 2396 + #: src/components/status.jsx:3088 2397 2397 msgid "Edit History" 2398 2398 msgstr "" 2399 2399 2400 - #: src/components/status.jsx:3073 2400 + #: src/components/status.jsx:3092 2401 2401 msgid "Failed to load history" 2402 2402 msgstr "" 2403 2403 2404 - #: src/components/status.jsx:3078 2404 + #: src/components/status.jsx:3097 2405 2405 #: src/pages/annual-report.jsx:45 2406 2406 msgid "Loading…" 2407 2407 msgstr "" 2408 2408 2409 - #: src/components/status.jsx:3314 2409 + #: src/components/status.jsx:3333 2410 2410 msgid "HTML Code" 2411 2411 msgstr "" 2412 2412 2413 - #: src/components/status.jsx:3331 2413 + #: src/components/status.jsx:3350 2414 2414 msgid "HTML code copied" 2415 2415 msgstr "" 2416 2416 2417 - #: src/components/status.jsx:3334 2417 + #: src/components/status.jsx:3353 2418 2418 msgid "Unable to copy HTML code" 2419 2419 msgstr "" 2420 2420 2421 - #: src/components/status.jsx:3346 2421 + #: src/components/status.jsx:3365 2422 2422 msgid "Media attachments:" 2423 2423 msgstr "" 2424 2424 2425 - #: src/components/status.jsx:3368 2425 + #: src/components/status.jsx:3387 2426 2426 msgid "Account Emojis:" 2427 2427 msgstr "" 2428 2428 2429 - #: src/components/status.jsx:3399 2430 - #: src/components/status.jsx:3444 2429 + #: src/components/status.jsx:3418 2430 + #: src/components/status.jsx:3463 2431 2431 msgid "static URL" 2432 2432 msgstr "" 2433 2433 2434 - #: src/components/status.jsx:3413 2434 + #: src/components/status.jsx:3432 2435 2435 msgid "Emojis:" 2436 2436 msgstr "" 2437 2437 2438 - #: src/components/status.jsx:3458 2438 + #: src/components/status.jsx:3477 2439 2439 msgid "Notes:" 2440 2440 msgstr "" 2441 2441 2442 - #: src/components/status.jsx:3462 2442 + #: src/components/status.jsx:3481 2443 2443 msgid "This is static, unstyled and scriptless. You may need to apply your own styles and edit as needed." 2444 2444 msgstr "" 2445 2445 2446 - #: src/components/status.jsx:3468 2446 + #: src/components/status.jsx:3487 2447 2447 msgid "Polls are not interactive, becomes a list with vote counts." 2448 2448 msgstr "" 2449 2449 2450 - #: src/components/status.jsx:3473 2450 + #: src/components/status.jsx:3492 2451 2451 msgid "Media attachments can be images, videos, audios or any file types." 2452 2452 msgstr "" 2453 2453 2454 - #: src/components/status.jsx:3479 2454 + #: src/components/status.jsx:3498 2455 2455 msgid "Post could be edited or deleted later." 2456 2456 msgstr "" 2457 2457 2458 - #: src/components/status.jsx:3485 2458 + #: src/components/status.jsx:3504 2459 2459 msgid "Preview" 2460 2460 msgstr "" 2461 2461 2462 - #: src/components/status.jsx:3494 2462 + #: src/components/status.jsx:3513 2463 2463 msgid "Note: This preview is lightly styled." 2464 2464 msgstr "" 2465 2465 2466 2466 #. [Name] [Visibility icon] boosted 2467 - #: src/components/status.jsx:3747 2467 + #: src/components/status.jsx:3766 2468 2468 msgid "<0/> <1/> boosted" 2469 2469 msgstr "" 2470 2470 2471 2471 #: src/components/timeline.jsx:479 2472 - #: src/pages/settings.jsx:1174 2472 + #: src/pages/settings.jsx:1179 2473 2473 msgid "New posts" 2474 2474 msgstr "" 2475 2475 ··· 2501 2501 msgid "<0>Filtered</0>: <1>{0}</1>" 2502 2502 msgstr "" 2503 2503 2504 - #: src/components/translation-block.jsx:172 2504 + #: src/components/translation-block.jsx:192 2505 2505 msgid "Auto-translated from {sourceLangText}" 2506 2506 msgstr "" 2507 2507 2508 - #: src/components/translation-block.jsx:210 2508 + #: src/components/translation-block.jsx:230 2509 2509 msgid "Translating…" 2510 2510 msgstr "" 2511 2511 2512 - #: src/components/translation-block.jsx:213 2512 + #: src/components/translation-block.jsx:233 2513 2513 msgid "Translate from {sourceLangText} (auto-detected)" 2514 2514 msgstr "" 2515 2515 2516 - #: src/components/translation-block.jsx:214 2516 + #: src/components/translation-block.jsx:234 2517 2517 msgid "Translate from {sourceLangText}" 2518 2518 msgstr "" 2519 2519 2520 2520 #. placeholder {0}: detectedLang ?? '…' 2521 - #: src/components/translation-block.jsx:242 2521 + #: src/components/translation-block.jsx:262 2522 2522 msgid "Auto ({0})" 2523 2523 msgstr "" 2524 2524 2525 - #: src/components/translation-block.jsx:255 2525 + #: src/components/translation-block.jsx:275 2526 2526 msgid "Failed to translate" 2527 2527 msgstr "" 2528 2528 ··· 3342 3342 msgstr "" 3343 3343 3344 3344 #: src/pages/notifications.jsx:673 3345 - #: src/pages/settings.jsx:1162 3345 + #: src/pages/settings.jsx:1167 3346 3346 msgid "Follow requests" 3347 3347 msgstr "" 3348 3348 ··· 3559 3559 msgid "Enter your search term or paste a URL above to get started." 3560 3560 msgstr "" 3561 3561 3562 - #: src/pages/settings.jsx:84 3562 + #: src/pages/settings.jsx:92 3563 3563 msgid "Settings" 3564 3564 msgstr "" 3565 3565 3566 - #: src/pages/settings.jsx:93 3566 + #: src/pages/settings.jsx:101 3567 3567 msgid "Appearance" 3568 3568 msgstr "" 3569 3569 3570 - #: src/pages/settings.jsx:169 3570 + #: src/pages/settings.jsx:177 3571 3571 msgid "Light" 3572 3572 msgstr "" 3573 3573 3574 - #: src/pages/settings.jsx:180 3574 + #: src/pages/settings.jsx:188 3575 3575 msgid "Dark" 3576 3576 msgstr "" 3577 3577 3578 - #: src/pages/settings.jsx:193 3578 + #: src/pages/settings.jsx:201 3579 3579 msgid "Auto" 3580 3580 msgstr "" 3581 3581 3582 - #: src/pages/settings.jsx:203 3582 + #: src/pages/settings.jsx:211 3583 3583 msgid "Text size" 3584 3584 msgstr "" 3585 3585 3586 3586 #. Preview of one character, in smallest size 3587 3587 #. Preview of one character, in largest size 3588 - #: src/pages/settings.jsx:208 3589 - #: src/pages/settings.jsx:233 3588 + #: src/pages/settings.jsx:216 3589 + #: src/pages/settings.jsx:241 3590 3590 msgid "A" 3591 3591 msgstr "" 3592 3592 3593 - #: src/pages/settings.jsx:247 3593 + #: src/pages/settings.jsx:255 3594 3594 msgid "Display language" 3595 3595 msgstr "" 3596 3596 3597 - #: src/pages/settings.jsx:256 3597 + #: src/pages/settings.jsx:264 3598 3598 msgid "Volunteer translations" 3599 3599 msgstr "Volunteer translations" 3600 3600 3601 - #: src/pages/settings.jsx:267 3601 + #: src/pages/settings.jsx:275 3602 3602 msgid "Posting" 3603 3603 msgstr "" 3604 3604 3605 - #: src/pages/settings.jsx:274 3605 + #: src/pages/settings.jsx:282 3606 3606 msgid "Default visibility" 3607 3607 msgstr "" 3608 3608 3609 - #: src/pages/settings.jsx:275 3610 - #: src/pages/settings.jsx:321 3609 + #: src/pages/settings.jsx:283 3610 + #: src/pages/settings.jsx:329 3611 3611 msgid "Synced" 3612 3612 msgstr "" 3613 3613 3614 - #: src/pages/settings.jsx:300 3614 + #: src/pages/settings.jsx:308 3615 3615 msgid "Failed to update posting privacy" 3616 3616 msgstr "" 3617 3617 3618 - #: src/pages/settings.jsx:323 3618 + #: src/pages/settings.jsx:331 3619 3619 msgid "Synced to your instance server's settings. <0>Go to your instance ({instance}) for more settings.</0>" 3620 3620 msgstr "" 3621 3621 3622 - #: src/pages/settings.jsx:338 3622 + #: src/pages/settings.jsx:346 3623 3623 msgid "Experiments" 3624 3624 msgstr "" 3625 3625 3626 - #: src/pages/settings.jsx:351 3626 + #: src/pages/settings.jsx:359 3627 3627 msgid "Auto refresh timeline posts" 3628 3628 msgstr "" 3629 3629 3630 - #: src/pages/settings.jsx:363 3630 + #: src/pages/settings.jsx:371 3631 3631 msgid "Boosts carousel" 3632 3632 msgstr "" 3633 3633 3634 - #: src/pages/settings.jsx:379 3634 + #: src/pages/settings.jsx:388 3635 3635 msgid "Post translation" 3636 3636 msgstr "" 3637 3637 3638 - #: src/pages/settings.jsx:390 3638 + #: src/pages/settings.jsx:399 3639 3639 msgid "Translate to " 3640 3640 msgstr "Translate to " 3641 3641 3642 - #: src/pages/settings.jsx:401 3642 + #: src/pages/settings.jsx:410 3643 3643 msgid "System language ({systemTargetLanguageText})" 3644 3644 msgstr "" 3645 3645 3646 - #. placeholder {0}: snapStates.settings.contentTranslationHideLanguages.length 3647 - #: src/pages/settings.jsx:427 3646 + #. placeholder {0}: snapStates.settings.contentTranslationHideLanguages .length 3647 + #: src/pages/settings.jsx:436 3648 3648 msgid "{0, plural, =0 {Hide \"Translate\" button for:} other {Hide \"Translate\" button for (#):}}" 3649 3649 msgstr "" 3650 3650 3651 - #: src/pages/settings.jsx:481 3652 - msgid "Note: This feature uses external translation services, powered by <0>Lingva API</0> & <1>Lingva Translate</1>." 3653 - msgstr "" 3651 + #: src/pages/settings.jsx:491 3652 + msgid "Note: This feature uses external translation services, powered by <0>{TRANSLATION_API_NAME}</0>." 3653 + msgstr "Note: This feature uses external translation services, powered by <0>{TRANSLATION_API_NAME}</0>." 3654 3654 3655 - #: src/pages/settings.jsx:515 3655 + #: src/pages/settings.jsx:519 3656 3656 msgid "Auto inline translation" 3657 3657 msgstr "" 3658 3658 3659 - #: src/pages/settings.jsx:519 3659 + #: src/pages/settings.jsx:523 3660 3660 msgid "Automatically show translation for posts in timeline. Only works for <0>short</0> posts without content warning, media and poll." 3661 3661 msgstr "" 3662 3662 3663 - #: src/pages/settings.jsx:539 3663 + #: src/pages/settings.jsx:544 3664 3664 msgid "GIF Picker for composer" 3665 3665 msgstr "" 3666 3666 3667 - #: src/pages/settings.jsx:543 3667 + #: src/pages/settings.jsx:548 3668 3668 msgid "Note: This feature uses external GIF search service, powered by <0>GIPHY</0>. G-rated (suitable for viewing by all ages), tracking parameters are stripped, referrer information is omitted from requests, but search queries and IP address information will still reach their servers." 3669 3669 msgstr "" 3670 3670 3671 - #: src/pages/settings.jsx:572 3671 + #: src/pages/settings.jsx:577 3672 3672 msgid "Image description generator" 3673 3673 msgstr "" 3674 3674 3675 - #: src/pages/settings.jsx:577 3675 + #: src/pages/settings.jsx:582 3676 3676 msgid "Only for new images while composing new posts." 3677 3677 msgstr "" 3678 3678 3679 - #: src/pages/settings.jsx:584 3679 + #: src/pages/settings.jsx:589 3680 3680 msgid "Note: This feature uses external AI service, powered by <0>img-alt-api</0>. May not work well. Only for images and in English." 3681 3681 msgstr "" 3682 3682 3683 - #: src/pages/settings.jsx:612 3683 + #: src/pages/settings.jsx:617 3684 3684 msgid "Server-side grouped notifications" 3685 3685 msgstr "" 3686 3686 3687 - #: src/pages/settings.jsx:616 3687 + #: src/pages/settings.jsx:621 3688 3688 msgid "Alpha-stage feature. Potentially improved grouping window but basic grouping logic." 3689 3689 msgstr "" 3690 3690 3691 - #: src/pages/settings.jsx:637 3691 + #: src/pages/settings.jsx:642 3692 3692 msgid "\"Cloud\" import/export for shortcuts settings" 3693 3693 msgstr "" 3694 3694 3695 - #: src/pages/settings.jsx:642 3695 + #: src/pages/settings.jsx:647 3696 3696 msgid "⚠️⚠️⚠️ Very experimental.<0/>Stored in your own profile’s notes. Profile (private) notes are mainly used for other profiles, and hidden for own profile." 3697 3697 msgstr "" 3698 3698 3699 - #: src/pages/settings.jsx:653 3699 + #: src/pages/settings.jsx:658 3700 3700 msgid "Note: This feature uses currently-logged-in instance server API." 3701 3701 msgstr "" 3702 3702 3703 - #: src/pages/settings.jsx:670 3703 + #: src/pages/settings.jsx:675 3704 3704 msgid "Cloak mode <0>(<1>Text</1> → <2>████</2>)</0>" 3705 3705 msgstr "" 3706 3706 3707 - #: src/pages/settings.jsx:679 3707 + #: src/pages/settings.jsx:684 3708 3708 msgid "Replace text as blocks, useful when taking screenshots, for privacy reasons." 3709 3709 msgstr "" 3710 3710 3711 - #: src/pages/settings.jsx:704 3711 + #: src/pages/settings.jsx:709 3712 3712 msgid "About" 3713 3713 msgstr "" 3714 3714 3715 - #: src/pages/settings.jsx:743 3715 + #: src/pages/settings.jsx:748 3716 3716 msgid "<0>Built</0> by <1>@cheeaun</1>" 3717 3717 msgstr "" 3718 3718 3719 - #: src/pages/settings.jsx:772 3719 + #: src/pages/settings.jsx:777 3720 3720 msgid "Sponsor" 3721 3721 msgstr "" 3722 3722 3723 - #: src/pages/settings.jsx:780 3723 + #: src/pages/settings.jsx:785 3724 3724 msgid "Donate" 3725 3725 msgstr "Donate" 3726 3726 3727 - #: src/pages/settings.jsx:792 3727 + #: src/pages/settings.jsx:797 3728 3728 msgid "Privacy Policy" 3729 3729 msgstr "" 3730 3730 3731 3731 #. placeholder {0}: WEBSITE.replace(/https?:\/\//g, '').replace(/\/$/, '') 3732 - #: src/pages/settings.jsx:799 3732 + #: src/pages/settings.jsx:804 3733 3733 msgid "<0>Site:</0> {0}" 3734 3734 msgstr "" 3735 3735 3736 3736 #. placeholder {0}: !__FAKE_COMMIT_HASH__ && ( <span class="ib insignificant"> ( <a href={`https://github.com/cheeaun/phanpy/commit/${__COMMIT_HASH__}`} target="_blank" rel="noopener" > <RelativeTime datetime={new Date(__BUILD_TIME__)} /> </a> ) </span> ) 3737 - #: src/pages/settings.jsx:806 3737 + #: src/pages/settings.jsx:811 3738 3738 msgid "<0>Version:</0> <1/> {0}" 3739 3739 msgstr "" 3740 3740 3741 - #: src/pages/settings.jsx:821 3741 + #: src/pages/settings.jsx:826 3742 3742 msgid "Version string copied" 3743 3743 msgstr "" 3744 3744 3745 - #: src/pages/settings.jsx:824 3745 + #: src/pages/settings.jsx:829 3746 3746 msgid "Unable to copy version string" 3747 3747 msgstr "" 3748 3748 3749 - #: src/pages/settings.jsx:1059 3750 3749 #: src/pages/settings.jsx:1064 3750 + #: src/pages/settings.jsx:1069 3751 3751 msgid "Failed to update subscription. Please try again." 3752 3752 msgstr "" 3753 3753 3754 - #: src/pages/settings.jsx:1070 3754 + #: src/pages/settings.jsx:1075 3755 3755 msgid "Failed to remove subscription. Please try again." 3756 3756 msgstr "" 3757 3757 3758 - #: src/pages/settings.jsx:1077 3758 + #: src/pages/settings.jsx:1082 3759 3759 msgid "Push Notifications (beta)" 3760 3760 msgstr "" 3761 3761 3762 - #: src/pages/settings.jsx:1099 3762 + #: src/pages/settings.jsx:1104 3763 3763 msgid "Push notifications are blocked. Please enable them in your browser settings." 3764 3764 msgstr "" 3765 3765 3766 3766 #. placeholder {0}: [ { value: 'all', label: t`anyone`, }, { value: 'followed', label: t`people I follow`, }, { value: 'follower', label: t`followers`, }, ].map((type) => ( <option value={type.value}>{type.label}</option> )) 3767 - #: src/pages/settings.jsx:1108 3767 + #: src/pages/settings.jsx:1113 3768 3768 msgid "Allow from <0>{0}</0>" 3769 3769 msgstr "" 3770 3770 3771 - #: src/pages/settings.jsx:1117 3771 + #: src/pages/settings.jsx:1122 3772 3772 msgid "anyone" 3773 3773 msgstr "" 3774 3774 3775 - #: src/pages/settings.jsx:1121 3775 + #: src/pages/settings.jsx:1126 3776 3776 msgid "people I follow" 3777 3777 msgstr "" 3778 3778 3779 - #: src/pages/settings.jsx:1125 3779 + #: src/pages/settings.jsx:1130 3780 3780 msgid "followers" 3781 3781 msgstr "" 3782 3782 3783 - #: src/pages/settings.jsx:1158 3783 + #: src/pages/settings.jsx:1163 3784 3784 msgid "Follows" 3785 3785 msgstr "" 3786 3786 3787 - #: src/pages/settings.jsx:1166 3787 + #: src/pages/settings.jsx:1171 3788 3788 msgid "Polls" 3789 3789 msgstr "" 3790 3790 3791 - #: src/pages/settings.jsx:1170 3791 + #: src/pages/settings.jsx:1175 3792 3792 msgid "Post edits" 3793 3793 msgstr "" 3794 3794 3795 - #: src/pages/settings.jsx:1191 3795 + #: src/pages/settings.jsx:1196 3796 3796 msgid "Push permission was not granted since your last login. You'll need to <0><1>log in</1> again to grant push permission</0>." 3797 3797 msgstr "" 3798 3798 3799 - #: src/pages/settings.jsx:1207 3799 + #: src/pages/settings.jsx:1212 3800 3800 msgid "NOTE: Push notifications only work for <0>one account</0>." 3801 3801 msgstr "" 3802 3802
+149 -144
src/pages/settings.jsx
··· 10 10 import LangSelector from '../components/lang-selector'; 11 11 import Link from '../components/link'; 12 12 import RelativeTime from '../components/relative-time'; 13 - import targetLanguages from '../data/lingva-target-languages'; 13 + import languages from '../data/translang-languages'; 14 14 import { api } from '../utils/api'; 15 15 import getTranslateTargetLanguage from '../utils/get-translate-target-language'; 16 16 import localeCode2Text from '../utils/localeCode2Text'; ··· 32 32 const { 33 33 PHANPY_WEBSITE: WEBSITE, 34 34 PHANPY_PRIVACY_POLICY_URL: PRIVACY_POLICY_URL, 35 + PHANPY_TRANSLANG_INSTANCES: TRANSLANG_INSTANCES, 35 36 PHANPY_IMG_ALT_API_URL: IMG_ALT_API_URL, 36 37 PHANPY_GIPHY_API_KEY: GIPHY_API_KEY, 37 38 } = import.meta.env; 39 + 40 + const targetLanguages = Object.entries(languages.tl).map(([code, name]) => ({ 41 + code, 42 + name, 43 + })); 44 + 45 + const TRANSLATION_API_NAME = 'TransLang API'; 38 46 39 47 function Settings({ onClose }) { 40 48 const { t } = useLingui(); ··· 363 371 <Trans>Boosts carousel</Trans> 364 372 </label> 365 373 </li> 366 - <li class="block"> 367 - <label> 368 - <input 369 - type="checkbox" 370 - checked={snapStates.settings.contentTranslation} 371 - onChange={(e) => { 372 - const { checked } = e.target; 373 - states.settings.contentTranslation = checked; 374 - if (!checked) { 375 - states.settings.contentTranslationTargetLanguage = null; 376 - } 377 - }} 378 - />{' '} 379 - <Trans>Post translation</Trans> 380 - </label> 381 - <div 382 - class={`sub-section ${ 383 - !snapStates.settings.contentTranslation 384 - ? 'more-insignificant' 385 - : '' 386 - }`} 387 - > 388 - <div> 389 - <label> 390 - <Trans>Translate to </Trans>{' '} 391 - <select 392 - value={targetLanguage || ''} 393 - disabled={!snapStates.settings.contentTranslation} 394 - style={{ width: '10em' }} 395 - onChange={(e) => { 396 - states.settings.contentTranslationTargetLanguage = 397 - e.target.value || null; 398 - }} 399 - > 400 - <option value=""> 401 - <Trans> 402 - System language ({systemTargetLanguageText}) 403 - </Trans> 404 - </option> 405 - <option disabled>──────────</option> 374 + {!!TRANSLANG_INSTANCES && ( 375 + <li class="block"> 376 + <label> 377 + <input 378 + type="checkbox" 379 + checked={snapStates.settings.contentTranslation} 380 + onChange={(e) => { 381 + const { checked } = e.target; 382 + states.settings.contentTranslation = checked; 383 + if (!checked) { 384 + states.settings.contentTranslationTargetLanguage = null; 385 + } 386 + }} 387 + />{' '} 388 + <Trans>Post translation</Trans> 389 + </label> 390 + <div 391 + class={`sub-section ${ 392 + !snapStates.settings.contentTranslation 393 + ? 'more-insignificant' 394 + : '' 395 + }`} 396 + > 397 + <div> 398 + <label> 399 + <Trans>Translate to </Trans>{' '} 400 + <select 401 + value={targetLanguage || ''} 402 + disabled={!snapStates.settings.contentTranslation} 403 + style={{ width: '10em' }} 404 + onChange={(e) => { 405 + states.settings.contentTranslationTargetLanguage = 406 + e.target.value || null; 407 + }} 408 + > 409 + <option value=""> 410 + <Trans> 411 + System language ({systemTargetLanguageText}) 412 + </Trans> 413 + </option> 414 + <option disabled>──────────</option> 415 + {targetLanguages.map((lang) => { 416 + const common = localeCode2Text({ 417 + code: lang.code, 418 + fallback: lang.name, 419 + }); 420 + const native = localeCode2Text({ 421 + code: lang.code, 422 + locale: lang.code, 423 + }); 424 + const showCommon = native && common !== native; 425 + return ( 426 + <option value={lang.code}> 427 + {showCommon ? `${native} - ${common}` : common} 428 + </option> 429 + ); 430 + })} 431 + </select> 432 + </label> 433 + </div> 434 + <hr /> 435 + <div class="checkbox-fieldset"> 436 + <Plural 437 + value={ 438 + snapStates.settings.contentTranslationHideLanguages 439 + .length 440 + } 441 + _0={`Hide "Translate" button for:`} 442 + other={`Hide "Translate" button for (#):`} 443 + /> 444 + <div class="checkbox-fields"> 406 445 {targetLanguages.map((lang) => { 407 446 const common = localeCode2Text({ 408 447 code: lang.code, ··· 412 451 code: lang.code, 413 452 locale: lang.code, 414 453 }); 415 - const showCommon = common !== native; 454 + const showCommon = native && common !== native; 416 455 return ( 417 - <option value={lang.code}> 418 - {showCommon ? `${native} - ${common}` : common} 419 - </option> 456 + <label> 457 + <input 458 + type="checkbox" 459 + checked={snapStates.settings.contentTranslationHideLanguages.includes( 460 + lang.code, 461 + )} 462 + onChange={(e) => { 463 + const { checked } = e.target; 464 + if (checked) { 465 + states.settings.contentTranslationHideLanguages.push( 466 + lang.code, 467 + ); 468 + } else { 469 + states.settings.contentTranslationHideLanguages = 470 + snapStates.settings.contentTranslationHideLanguages.filter( 471 + (code) => code !== lang.code, 472 + ); 473 + } 474 + }} 475 + />{' '} 476 + {showCommon ? ( 477 + <span> 478 + {native}{' '} 479 + <span class="insignificant ib">- {common}</span> 480 + </span> 481 + ) : ( 482 + common 483 + )} 484 + </label> 420 485 ); 421 486 })} 422 - </select> 423 - </label> 424 - </div> 425 - <hr /> 426 - <div class="checkbox-fieldset"> 427 - <Plural 428 - value={ 429 - snapStates.settings.contentTranslationHideLanguages.length 430 - } 431 - _0={`Hide "Translate" button for:`} 432 - other={`Hide "Translate" button for (#):`} 433 - /> 434 - <div class="checkbox-fields"> 435 - {targetLanguages.map((lang) => { 436 - const common = localeCode2Text({ 437 - code: lang.code, 438 - fallback: lang.name, 439 - }); 440 - const native = localeCode2Text({ 441 - code: lang.code, 442 - locale: lang.code, 443 - }); 444 - const showCommon = common !== native; 445 - return ( 446 - <label> 447 - <input 448 - type="checkbox" 449 - checked={snapStates.settings.contentTranslationHideLanguages.includes( 450 - lang.code, 451 - )} 452 - onChange={(e) => { 453 - const { checked } = e.target; 454 - if (checked) { 455 - states.settings.contentTranslationHideLanguages.push( 456 - lang.code, 457 - ); 458 - } else { 459 - states.settings.contentTranslationHideLanguages = 460 - snapStates.settings.contentTranslationHideLanguages.filter( 461 - (code) => code !== lang.code, 462 - ); 463 - } 464 - }} 465 - />{' '} 466 - {showCommon ? ( 467 - <span> 468 - {native}{' '} 469 - <span class="insignificant">- {common}</span> 470 - </span> 471 - ) : ( 472 - common 473 - )} 474 - </label> 475 - ); 476 - })} 487 + </div> 477 488 </div> 478 - </div> 479 - <p class="insignificant"> 480 - <small> 481 - <Trans> 482 - Note: This feature uses external translation services, 483 - powered by{' '} 484 - <a 485 - href="https://github.com/cheeaun/lingva-api" 486 - target="_blank" 487 - rel="noopener" 488 - > 489 - Lingva API 490 - </a>{' '} 491 - &amp;{' '} 492 - <a 493 - href="https://github.com/thedaviddelta/lingva-translate" 494 - target="_blank" 495 - rel="noopener" 496 - > 497 - Lingva Translate 498 - </a> 499 - . 500 - </Trans> 501 - </small> 502 - </p> 503 - <hr /> 504 - <div> 505 - <label> 506 - <input 507 - type="checkbox" 508 - checked={snapStates.settings.contentTranslationAutoInline} 509 - disabled={!snapStates.settings.contentTranslation} 510 - onChange={(e) => { 511 - states.settings.contentTranslationAutoInline = 512 - e.target.checked; 513 - }} 514 - />{' '} 515 - <Trans>Auto inline translation</Trans> 516 - </label> 517 489 <p class="insignificant"> 518 490 <small> 519 491 <Trans> 520 - Automatically show translation for posts in timeline. 521 - Only works for <b>short</b> posts without content 522 - warning, media and poll. 492 + Note: This feature uses external translation services, 493 + powered by{' '} 494 + <a 495 + href="https://github.com/cheeaun/translang-api" 496 + target="_blank" 497 + rel="noopener" 498 + > 499 + {TRANSLATION_API_NAME} 500 + </a> 501 + . 523 502 </Trans> 524 503 </small> 525 504 </p> 505 + <hr /> 506 + <div> 507 + <label> 508 + <input 509 + type="checkbox" 510 + checked={ 511 + snapStates.settings.contentTranslationAutoInline 512 + } 513 + disabled={!snapStates.settings.contentTranslation} 514 + onChange={(e) => { 515 + states.settings.contentTranslationAutoInline = 516 + e.target.checked; 517 + }} 518 + />{' '} 519 + <Trans>Auto inline translation</Trans> 520 + </label> 521 + <p class="insignificant"> 522 + <small> 523 + <Trans> 524 + Automatically show translation for posts in timeline. 525 + Only works for <b>short</b> posts without content 526 + warning, media and poll. 527 + </Trans> 528 + </small> 529 + </p> 530 + </div> 526 531 </div> 527 - </div> 528 - </li> 532 + </li> 533 + )} 529 534 {!!GIPHY_API_KEY && authenticated && ( 530 535 <li class="block"> 531 536 <label>
+8 -1
src/utils/get-translate-target-language.js
··· 1 - import translationTargetLanguages from '../data/lingva-target-languages'; 1 + import languages from '../data/translang-languages'; 2 2 3 3 import localeMatch from './locale-match'; 4 4 import mem from './mem'; 5 5 import states from './states'; 6 + 7 + const translationTargetLanguages = Object.entries(languages).map( 8 + ([code, { name }]) => ({ 9 + code, 10 + name, 11 + }), 12 + ); 6 13 7 14 const locales = mem(() => [ 8 15 new Intl.DateTimeFormat().resolvedOptions().locale,
+4
src/utils/localeCode2Text.js
··· 1 1 import { i18n } from '@lingui/core'; 2 2 3 + import translangLanguagesNative from '../data/translang-languages-native'; 4 + 3 5 import mem from './mem'; 4 6 5 7 // Some codes are not supported by Intl.DisplayNames ··· 28 30 if (!fallback) { 29 31 const anotherText = IntlDN(code).of(code); 30 32 if (anotherText !== code) return anotherText; 33 + const yetAnotherText = translangLanguagesNative?.[locale]; 34 + if (yetAnotherText !== code) return yetAnotherText; 31 35 } 32 36 return fallback || ''; 33 37 } catch (e) {