Time to migrate away from Lingva

pull/1126/head
Lim Chee Aun 2025-04-21 13:14:10 +08:00
rodzic de83092259
commit e586b77a3e
12 zmienionych plików z 1274 dodań i 402 usunięć

3
.env
Wyświetl plik

@ -1,4 +1,5 @@
PHANPY_CLIENT_NAME=Phanpy
PHANPY_WEBSITE=https://phanpy.social
PHANPY_LINGVA_INSTANCES="lingva.phanpy.social lingva.lunar.icu lingva.garudalinux.org translate.plausibility.cloud"
PHANPY_PRIVACY_POLICY_URL="https://github.com/cheeaun/phanpy/blob/main/PRIVACY.MD"
PHANPY_PRIVACY_POLICY_URL="https://github.com/cheeaun/phanpy/blob/main/PRIVACY.MD"
PHANPY_TRANSLANG_INSTANCES="translang.phanpy.social"

Wyświetl plik

@ -238,11 +238,15 @@ Available variables:
- This is applied with the `<meta>` tag on the client-side.
- The policy can also be set with `Referrer-Policy` header configured on the server-side (not this variable).
- Note that since Phanpy uses hash-based URLs, the referrer does not include the hash part.
- `PHANPY_LINGVA_INSTANCES` (optional, space-separated list, default: `lingva.phanpy.social [...hard-coded list of fallback instances]`):
- `PHANPY_LINGVA_INSTANCES` (**DEPRECATED**, optional, space-separated list, default: `lingva.phanpy.social [...hard-coded list of fallback instances]`):
- 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.
- 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)
- List of fallback instances hard-coded in `/.env`
- [↗️ List of lingva-translate instances](https://github.com/thedaviddelta/lingva-translate?tab=readme-ov-file#instances)
- `PHANPY_TRANSLANG_INSTANCES` (optional, space-separated list, default: `translang.phanpy.social`):
- 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.
- May specify a self-hosted Translating instance, powered by [translang-api](https://github.com/cheeaun/translang-api).
- List of instances hard-coded in `/.env`
- `PHANPY_IMG_ALT_API_URL` (optional, no defaults):
- API endpoint for self-hosted instance of [img-alt-api](https://github.com/cheeaun/img-alt-api).
- If provided, a setting will appear for users to enable the image description generator in the composer. Disabled by default.

Wyświetl plik

@ -0,0 +1,9 @@
import fs from 'fs';
fetch('https://translang.phanpy.social/api/v1/languages')
.then((response) => response.json())
.then((json) => {
const file = './src/data/translang-languages.json';
console.log(`Writing ${file}...`);
fs.writeFileSync(file, JSON.stringify(json, null, '\t'), 'utf8');
});

Wyświetl plik

@ -119,8 +119,17 @@ function getPollText(poll) {
)
.join('\n')}`;
}
function getPostText(status) {
const { spoilerText, content, poll } = status;
function getPostText(status, opts) {
const { maskCustomEmojis } = opts || {};
const { spoilerText, poll, emojis } = status;
let { content } = status;
if (maskCustomEmojis && emojis?.length) {
const emojisRegex = new RegExp(
`:(${emojis.map((e) => e.shortcode).join('|')}):`,
'g',
);
content = content.replace(emojisRegex, '⬚');
}
return (
(spoilerText ? `${spoilerText}\n\n` : '') +
getHTMLText(content) +
@ -139,8 +148,16 @@ function forgivingQSA(selectors = [], dom = document) {
return [];
}
function isTranslateble(content) {
function isTranslateble(content, emojis) {
if (!content) return false;
// Remove custom emojis
if (emojis?.length) {
const emojisRegex = new RegExp(
`:(${emojis.map((e) => e.shortcode).join('|')}):`,
'g',
);
content = content.replace(emojisRegex, '');
}
content = content.trim();
if (!content) return false;
const text = getHTMLText(content, {
@ -2212,7 +2229,7 @@ function Status({
/>
)}
{(((enableTranslate || inlineTranslate) &&
isTranslateble(content) &&
isTranslateble(content, emojis) &&
differentLanguage) ||
forceTranslate) && (
<TranslationBlock
@ -2220,7 +2237,9 @@ function Status({
mini={!isSizeLarge && !withinContext}
sourceLanguage={language}
autoDetected={languageAutoDetected}
text={getPostText(status)}
text={getPostText(status, {
maskCustomEmojis: true,
})}
/>
)}
{!previewMode &&

Wyświetl plik

@ -5,7 +5,7 @@ import pRetry from 'p-retry';
import pThrottle from 'p-throttle';
import { useEffect, useRef, useState } from 'preact/hooks';
import sourceLanguages from '../data/lingva-source-languages';
import languages from '../data/translang-languages';
import {
translate as browserTranslate,
supportsBrowserTranslator,
@ -18,9 +18,14 @@ import Icon from './icon';
import LazyShazam from './lazy-shazam';
import Loader from './loader';
const { PHANPY_LINGVA_INSTANCES } = import.meta.env;
const LINGVA_INSTANCES = PHANPY_LINGVA_INSTANCES
? PHANPY_LINGVA_INSTANCES.split(/\s+/)
const sourceLanguages = Object.entries(languages.sl).map(([code, name]) => ({
code,
name,
}));
const { PHANPY_TRANSLANG_INSTANCES } = import.meta.env;
const TRANSLANG_INSTANCES = PHANPY_TRANSLANG_INSTANCES
? PHANPY_TRANSLANG_INSTANCES.split(/\s+/)
: [];
const throttle = pThrottle({
@ -28,50 +33,70 @@ const throttle = pThrottle({
interval: 2000,
});
let currentLingvaInstance = 0;
const TRANSLATED_MAX_AGE = 1000 * 60 * 60; // 1 hour
let currentTranslangInstance = 0;
function _lingvaTranslate(text, source, target) {
function _translangTranslate(text, source, target) {
console.log('TRANSLATE', text, source, target);
const fetchCall = () => {
let instance = LINGVA_INSTANCES[currentLingvaInstance];
return fetch(
`https://${instance}/api/v1/${source}/${target}/${encodeURIComponent(
text,
)}`,
)
let instance = TRANSLANG_INSTANCES[currentTranslangInstance];
const tooLong = text.length > 2000;
let fetchPromise;
if (tooLong) {
// POST
fetchPromise = fetch(`https://${instance}/api/v1/translate`, {
method: 'POST',
priority: 'low',
referrerPolicy: 'no-referrer',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sl: source,
tl: target,
text,
}),
});
} else {
// GET
fetchPromise = fetch(
`https://${instance}/api/v1/translate?sl=${encodeURIComponent(source)}&tl=${encodeURIComponent(target)}&text=${encodeURIComponent(text)}`,
{
priority: 'low',
referrerPolicy: 'no-referrer',
},
);
}
return fetchPromise
.then((res) => {
if (!res.ok) throw new Error(res.statusText);
return res.json();
})
.then((res) => {
return {
provider: 'lingva',
content: res.translation,
detectedSourceLanguage: res.info?.detectedSource,
info: res.info,
provider: 'translang',
content: res.translated_text,
detectedSourceLanguage: res.detected_language,
pronunciation: res.pronunciation,
};
});
};
return pRetry(fetchCall, {
retries: 3,
onFailedAttempt: (e) => {
currentLingvaInstance =
(currentLingvaInstance + 1) % LINGVA_INSTANCES.length;
currentTranslangInstance =
(currentTranslangInstance + 1) % TRANSLANG_INSTANCES.length;
console.log(
'Retrying translation with another instance',
currentLingvaInstance,
currentTranslangInstance,
);
},
});
// return masto.v1.statuses.$select(id).translate({
// lang: DEFAULT_LANG,
// });
}
const TRANSLATED_MAX_AGE = 1000 * 60 * 60; // 1 hour
const lingvaTranslate = pmem(_lingvaTranslate, {
const translangTranslate = pmem(_translangTranslate, {
maxAge: TRANSLATED_MAX_AGE,
});
const throttledLingvaTranslate = pmem(throttle(lingvaTranslate), {
const throttledTranslangTranslate = pmem(throttle(translangTranslate), {
// I know, this is double-layered memoization
maxAge: TRANSLATED_MAX_AGE,
});
@ -99,11 +124,6 @@ function TranslationBlock({
const apiSourceLang = useRef('auto');
if (!onTranslate) {
// onTranslate = supportsBrowserTranslator
// ? browserTranslate
// : mini
// ? throttledLingvaTranslate
// : lingvaTranslate;
onTranslate = async (...args) => {
if (supportsBrowserTranslator) {
const result = await browserTranslate(...args);
@ -112,8 +132,8 @@ function TranslationBlock({
}
}
return mini
? await throttledLingvaTranslate(...args)
: await lingvaTranslate(...args);
? await throttledTranslangTranslate(...args)
: await translangTranslate(...args);
};
}
@ -127,8 +147,8 @@ function TranslationBlock({
const detectedLangText = localeCode2Text(detectedSourceLanguage);
setDetectedLang(detectedLangText);
}
if (provider === 'lingva') {
const pronunciation = props?.info?.pronunciation?.query;
if (provider === 'translang') {
const pronunciation = props?.pronunciation;
if (pronunciation) {
setPronunciationContent(pronunciation);
}
@ -235,14 +255,14 @@ function TranslationBlock({
code: l.code,
locale: l.code,
});
const showCommon = common !== native;
const showCommon = native && common !== native;
return (
<option value={l.code}>
{l.code === 'auto'
? t`Auto (${detectedLang ?? '…'})`
: showCommon
? `${native} - ${common}`
: native}
: common}
</option>
);
})}
@ -280,4 +300,4 @@ function TranslationBlock({
);
}
export default LINGVA_INSTANCES?.length ? TranslationBlock : () => null;
export default TRANSLANG_INSTANCES?.length ? TranslationBlock : () => null;

Wyświetl plik

@ -0,0 +1,46 @@
[
"ca-ES",
<<<<<<< HEAD
"es-ES",
"fi-FI",
"gl-ES",
"it-IT",
"pt-BR",
"pt-PT",
"zh-CN",
"eo-UY",
"ru-RU",
"eu-ES",
"lt-LT",
"kab",
"de-DE",
"uk-UA",
"fr-FR",
"ko-KR",
"cs-CZ",
"nl-NL",
"fa-IR",
"pl-PL"
=======
"cs-CZ",
"de-DE",
"eo-UY",
"es-ES",
"eu-ES",
"fa-IR",
"fi-FI",
"fr-FR",
"gl-ES",
"it-IT",
"kab",
"ko-KR",
"lt-LT",
"nl-NL",
"pl-PL",
"pt-BR",
"pt-PT",
"ru-RU",
"uk-UA",
"zh-CN"
>>>>>>> 8eeab341 (Move percentage threshold to build time)
]

Wyświetl plik

@ -0,0 +1,251 @@
{
"ab": "Аҧсшәа",
"ace": "Acèh",
"ach": "Lëbacoli",
"aa": "Afár",
"af": "Afrikaans",
"sq": "Shqip",
"alz": "Dhalur",
"am": "አማርኛ",
"ar": "العربية",
"hy": "Հայերեն",
"as": "অসমীয়া",
"av": "Авар мацӀ",
"awa": "अवधी",
"ay": "Aymar aru",
"az": "Azərbaycanca",
"ban": "Basa Bali",
"bal": "بلوچی",
"bm": "Bamanankan",
"bci": "Baoulé",
"ba": "Башҡортса",
"eu": "Euskara",
"btx": "Batak Karo",
"bts": "Batak Simalungun",
"bbc": "Batak Toba",
"be": "Беларуская",
"bem": "Ichibemba",
"bn": "বাংলা",
"bew": "Betawi",
"bho": "भोजपुरी",
"bik": "Bikol",
"bs": "Bosanski",
"br": "Brezhoneg",
"bg": "Български",
"bua": "Буряад",
"yue": "粵語",
"ca": "Català",
"ceb": "Cebuano",
"ch": "Chamoru",
"ce": "Нохчийн мотт",
"ny": "Chichewa",
"zh-CN": "简体中文",
"zh-TW": "繁體中文",
"chk": "Kapasen Chuuk",
"cv": "Чӑвашла",
"co": "Corsu",
"crh": "Qırımtatarca (Кирилл)",
"crh-Latn": "Qırımtatarca (Latin)",
"hr": "Hrvatski",
"cs": "Čeština",
"da": "Dansk",
"fa-AF": "درى",
"dv": "ދިވެހި",
"din": "Thuɔŋjäŋ",
"doi": "डोगरी",
"dov": "Dombe",
"nl": "Nederlands",
"dyu": "Jula",
"dz": "རྫོང་ཁ",
"en": "English",
"eo": "Esperanto",
"et": "Eesti",
"ee": "Eʋegbe",
"fo": "Føroyskt",
"fj": "Vosa Vakaviti",
"tl": "Tagalog",
"fi": "Suomi",
"fon": "Fon",
"fr": "Français",
"fr-CA": "Français (Canada)",
"fy": "Frysk",
"fur": "Furlan",
"ff": "Fulfulde",
"gaa": "Gã",
"gl": "Galego",
"ka": "ქართული",
"de": "Deutsch",
"el": "Ελληνικά",
"gn": "Avañe'ẽ",
"gu": "ગુજરાતી",
"ht": "Kreyòl ayisyen",
"cnh": "Hakha Chin",
"ha": "Hausa",
"haw": "ʻŌlelo Hawaiʻi",
"iw": "עברית",
"hil": "Hiligaynon",
"hi": "हिन्दी",
"hmn": "Hmoob",
"hu": "Magyar",
"hrx": "Hunsrik",
"iba": "Iban",
"is": "Íslenska",
"ig": "Igbo",
"ilo": "Iloko",
"id": "Bahasa Indonesia",
"iu-Latn": "Inuktitut (Latin)",
"iu": "ᐃᓄᒃᑎᑐᑦ",
"ga": "Gaeilge",
"it": "Italiano",
"jam": "Patwa",
"ja": "日本語",
"jw": "Basa Jawa",
"kac": "Jinghpaw",
"kl": "Kalaallisut",
"kn": "ಕನ್ನಡ",
"kr": "Kanuri",
"pam": "Kapampangan",
"kk": "Қазақша",
"kha": "Khasi",
"km": "ភាសាខ្មែរ",
"cgg": "Rukiga",
"kg": "Kikongo",
"rw": "Kinyarwanda",
"ktu": "Kituba",
"trp": "Kokborok",
"kv": "Коми кыв",
"gom": "कोंकणी",
"ko": "한국어",
"kri": "Krio",
"ku": "Kurdî (Kurmancî)",
"ckb": "کوردی (سۆرانی)",
"ky": "Кыргызча",
"lo": "ລາວ",
"ltg": "Latgaļu",
"la": "Latina",
"lv": "Latviešu",
"lij": "Ligure",
"li": "Limburgs",
"ln": "Lingála",
"lt": "Lietuvių",
"lmo": "Lombard",
"lg": "Luganda",
"luo": "Dholuo",
"lb": "Lëtzebuergesch",
"mk": "Македонски",
"mad": "Madhurâ",
"mai": "मैथिली",
"mak": "Makassar",
"mg": "Malagasy",
"ms": "Bahasa Melayu",
"ms-Arab": "بهاس ملايو",
"ml": "മലയാളം",
"mt": "Malti",
"mam": "Mam",
"gv": "Gaelg",
"mi": "Te Reo Māori",
"mr": "मराठी",
"mh": "Kajin M̧ajeļ",
"mwr": "मारवाड़ी",
"mfe": "Kreol Morisien",
"chm": "Олык марий",
"mni-Mtei": "ꯃꯤꯇꯩꯂꯣꯟ",
"min": "Baso Minang",
"lus": "Mizo ṭawng",
"mn": "Монгол",
"my": "မြန်မာစာ",
"nhe": "Náhuatl",
"ndc-ZW": "Ndau",
"nr": "isiNdebele",
"new": "नेपाल भाषा",
"ne": "नेपाली",
"bm-Nkoo": "ߒߞߏ",
"no": "Norsk",
"nus": "Thok Nath",
"oc": "Occitan",
"or": "ଓଡ଼ିଆ",
"om": "Afaan Oromoo",
"os": "Ирон æвзаг",
"pag": "Pangasinan",
"pap": "Papiamentu",
"ps": "پښتو",
"fa": "فارسی",
"pl": "Polski",
"pt": "Português (Brasil)",
"pt-PT": "Português (Portugal)",
"pa": "ਪੰਜਾਬੀ",
"pa-Arab": "پنجابی",
"qu": "Runa Simi",
"kek": "Qʼeqchiʼ",
"rom": "Romani čhib",
"ro": "Română",
"rn": "Ikirundi",
"ru": "Русский",
"se": "Davvisámegiella",
"sm": "Gagana Samoa",
"sg": "Sängö",
"sa": "संस्कृतम्",
"sat-Latn": "Santali (Latin)",
"sat": "ᱥᱟᱱᱛᱟᱲᱤ",
"gd": "Gàidhlig",
"nso": "Sepedi",
"sr": "Српски",
"st": "Sesotho",
"crs": "Kreol seselwa",
"shn": "လိၵ်ႈတႆး",
"sn": "chiShona",
"scn": "Sicilianu",
"szl": "Ślōnskŏ",
"sd": "سنڌي",
"si": "සිංහල",
"sk": "Slovenčina",
"sl": "Slovenščina",
"so": "Soomaali",
"es": "Español",
"su": "Basa Sunda",
"sus": "Susu",
"sw": "Kiswahili",
"ss": "siSwati",
"sv": "Svenska",
"ty": "Reo Tahiti",
"tg": "Тоҷикӣ",
"ber-Latn": "Tamazight (Latin)",
"ber": "ⵜⴰⵎⴰⵣⵉⵖⵜ",
"ta": "தமிழ்",
"tt": "Татарча",
"te": "తెలుగు",
"tet": "Tetun",
"th": "ไทย",
"bo": "བོད་ཡིག",
"ti": "ትግርኛ",
"tiv": "Tiv",
"tpi": "Tok Pisin",
"to": "Lea fakatonga",
"lua": "Tshiluba",
"ts": "Xitsonga",
"tn": "Setswana",
"tcy": "ತುಳು",
"tum": "chiTumbuka",
"tr": "Türkçe",
"tk": "Türkmençe",
"tyv": "Тыва дыл",
"ak": "Akankasa",
"udm": "Удмурт кыл",
"uk": "Українська",
"ur": "اردو",
"ug": "ئۇيغۇرچە",
"uz": "Oʻzbekcha",
"ve": "Tshivenḓa",
"vec": "Vèneto",
"vi": "Tiếng Việt",
"war": "Winaray",
"cy": "Cymraeg",
"wo": "Wolof",
"xh": "isiXhosa",
"sah": "Саха тыла",
"yi": "ייִדיש",
"yo": "Yorùbá",
"yua": "Màaya T'àan",
"zap": "Didxazá",
"zu": "isiZulu"
}

Wyświetl plik

@ -0,0 +1,506 @@
{
"sl": {
"auto": "Detect language",
"ab": "Abkhaz",
"ace": "Acehnese",
"ach": "Acholi",
"aa": "Afar",
"af": "Afrikaans",
"sq": "Albanian",
"alz": "Alur",
"am": "Amharic",
"ar": "Arabic",
"hy": "Armenian",
"as": "Assamese",
"av": "Avar",
"awa": "Awadhi",
"ay": "Aymara",
"az": "Azerbaijani",
"ban": "Balinese",
"bal": "Baluchi",
"bm": "Bambara",
"bci": "Baoulé",
"ba": "Bashkir",
"eu": "Basque",
"btx": "Batak Karo",
"bts": "Batak Simalungun",
"bbc": "Batak Toba",
"be": "Belarusian",
"bem": "Bemba",
"bn": "Bengali",
"bew": "Betawi",
"bho": "Bhojpuri",
"bik": "Bikol",
"bs": "Bosnian",
"br": "Breton",
"bg": "Bulgarian",
"bua": "Buryat",
"yue": "Cantonese",
"ca": "Catalan",
"ceb": "Cebuano",
"ch": "Chamorro",
"ce": "Chechen",
"ny": "Chichewa",
"zh-CN": "Chinese (Simplified)",
"zh-TW": "Chinese (Traditional)",
"chk": "Chuukese",
"cv": "Chuvash",
"co": "Corsican",
"crh": "Crimean Tatar (Cyrillic)",
"crh-Latn": "Crimean Tatar (Latin)",
"hr": "Croatian",
"cs": "Czech",
"da": "Danish",
"fa-AF": "Dari",
"dv": "Dhivehi",
"din": "Dinka",
"doi": "Dogri",
"dov": "Dombe",
"nl": "Dutch",
"dyu": "Dyula",
"dz": "Dzongkha",
"en": "English",
"eo": "Esperanto",
"et": "Estonian",
"ee": "Ewe",
"fo": "Faroese",
"fj": "Fijian",
"tl": "Filipino",
"fi": "Finnish",
"fon": "Fon",
"fr": "French",
"fr-CA": "French (Canada)",
"fy": "Frisian",
"fur": "Friulian",
"ff": "Fulani",
"gaa": "Ga",
"gl": "Galician",
"ka": "Georgian",
"de": "German",
"el": "Greek",
"gn": "Guarani",
"gu": "Gujarati",
"ht": "Haitian Creole",
"cnh": "Hakha Chin",
"ha": "Hausa",
"haw": "Hawaiian",
"iw": "Hebrew",
"hil": "Hiligaynon",
"hi": "Hindi",
"hmn": "Hmong",
"hu": "Hungarian",
"hrx": "Hunsrik",
"iba": "Iban",
"is": "Icelandic",
"ig": "Igbo",
"ilo": "Ilocano",
"id": "Indonesian",
"iu-Latn": "Inuktut (Latin)",
"iu": "Inuktut (Syllabics)",
"ga": "Irish",
"it": "Italian",
"jam": "Jamaican Patois",
"ja": "Japanese",
"jw": "Javanese",
"kac": "Jingpo",
"kl": "Kalaallisut",
"kn": "Kannada",
"kr": "Kanuri",
"pam": "Kapampangan",
"kk": "Kazakh",
"kha": "Khasi",
"km": "Khmer",
"cgg": "Kiga",
"kg": "Kikongo",
"rw": "Kinyarwanda",
"ktu": "Kituba",
"trp": "Kokborok",
"kv": "Komi",
"gom": "Konkani",
"ko": "Korean",
"kri": "Krio",
"ku": "Kurdish (Kurmanji)",
"ckb": "Kurdish (Sorani)",
"ky": "Kyrgyz",
"lo": "Lao",
"ltg": "Latgalian",
"la": "Latin",
"lv": "Latvian",
"lij": "Ligurian",
"li": "Limburgish",
"ln": "Lingala",
"lt": "Lithuanian",
"lmo": "Lombard",
"lg": "Luganda",
"luo": "Luo",
"lb": "Luxembourgish",
"mk": "Macedonian",
"mad": "Madurese",
"mai": "Maithili",
"mak": "Makassar",
"mg": "Malagasy",
"ms": "Malay",
"ms-Arab": "Malay (Jawi)",
"ml": "Malayalam",
"mt": "Maltese",
"mam": "Mam",
"gv": "Manx",
"mi": "Maori",
"mr": "Marathi",
"mh": "Marshallese",
"mwr": "Marwadi",
"mfe": "Mauritian Creole",
"chm": "Meadow Mari",
"mni-Mtei": "Meiteilon (Manipuri)",
"min": "Minang",
"lus": "Mizo",
"mn": "Mongolian",
"my": "Myanmar (Burmese)",
"nhe": "Nahuatl (Eastern Huasteca)",
"ndc-ZW": "Ndau",
"nr": "Ndebele (South)",
"new": "Nepalbhasa (Newari)",
"ne": "Nepali",
"bm-Nkoo": "NKo",
"no": "Norwegian",
"nus": "Nuer",
"oc": "Occitan",
"or": "Odia (Oriya)",
"om": "Oromo",
"os": "Ossetian",
"pag": "Pangasinan",
"pap": "Papiamento",
"ps": "Pashto",
"fa": "Persian",
"pl": "Polish",
"pt": "Portuguese (Brazil)",
"pt-PT": "Portuguese (Portugal)",
"pa": "Punjabi (Gurmukhi)",
"pa-Arab": "Punjabi (Shahmukhi)",
"qu": "Quechua",
"kek": "Qʼeqchiʼ",
"rom": "Romani",
"ro": "Romanian",
"rn": "Rundi",
"ru": "Russian",
"se": "Sami (North)",
"sm": "Samoan",
"sg": "Sango",
"sa": "Sanskrit",
"sat-Latn": "Santali (Latin)",
"sat": "Santali (Ol Chiki)",
"gd": "Scots Gaelic",
"nso": "Sepedi",
"sr": "Serbian",
"st": "Sesotho",
"crs": "Seychellois Creole",
"shn": "Shan",
"sn": "Shona",
"scn": "Sicilian",
"szl": "Silesian",
"sd": "Sindhi",
"si": "Sinhala",
"sk": "Slovak",
"sl": "Slovenian",
"so": "Somali",
"es": "Spanish",
"su": "Sundanese",
"sus": "Susu",
"sw": "Swahili",
"ss": "Swati",
"sv": "Swedish",
"ty": "Tahitian",
"tg": "Tajik",
"ber-Latn": "Tamazight",
"ber": "Tamazight (Tifinagh)",
"ta": "Tamil",
"tt": "Tatar",
"te": "Telugu",
"tet": "Tetum",
"th": "Thai",
"bo": "Tibetan",
"ti": "Tigrinya",
"tiv": "Tiv",
"tpi": "Tok Pisin",
"to": "Tongan",
"lua": "Tshiluba",
"ts": "Tsonga",
"tn": "Tswana",
"tcy": "Tulu",
"tum": "Tumbuka",
"tr": "Turkish",
"tk": "Turkmen",
"tyv": "Tuvan",
"ak": "Twi",
"udm": "Udmurt",
"uk": "Ukrainian",
"ur": "Urdu",
"ug": "Uyghur",
"uz": "Uzbek",
"ve": "Venda",
"vec": "Venetian",
"vi": "Vietnamese",
"war": "Waray",
"cy": "Welsh",
"wo": "Wolof",
"xh": "Xhosa",
"sah": "Yakut",
"yi": "Yiddish",
"yo": "Yoruba",
"yua": "Yucatec Maya",
"zap": "Zapotec",
"zu": "Zulu"
},
"tl": {
"ab": "Abkhaz",
"ace": "Acehnese",
"ach": "Acholi",
"aa": "Afar",
"af": "Afrikaans",
"sq": "Albanian",
"alz": "Alur",
"am": "Amharic",
"ar": "Arabic",
"hy": "Armenian",
"as": "Assamese",
"av": "Avar",
"awa": "Awadhi",
"ay": "Aymara",
"az": "Azerbaijani",
"ban": "Balinese",
"bal": "Baluchi",
"bm": "Bambara",
"bci": "Baoulé",
"ba": "Bashkir",
"eu": "Basque",
"btx": "Batak Karo",
"bts": "Batak Simalungun",
"bbc": "Batak Toba",
"be": "Belarusian",
"bem": "Bemba",
"bn": "Bengali",
"bew": "Betawi",
"bho": "Bhojpuri",
"bik": "Bikol",
"bs": "Bosnian",
"br": "Breton",
"bg": "Bulgarian",
"bua": "Buryat",
"yue": "Cantonese",
"ca": "Catalan",
"ceb": "Cebuano",
"ch": "Chamorro",
"ce": "Chechen",
"ny": "Chichewa",
"zh-CN": "Chinese (Simplified)",
"zh-TW": "Chinese (Traditional)",
"chk": "Chuukese",
"cv": "Chuvash",
"co": "Corsican",
"crh": "Crimean Tatar (Cyrillic)",
"crh-Latn": "Crimean Tatar (Latin)",
"hr": "Croatian",
"cs": "Czech",
"da": "Danish",
"fa-AF": "Dari",
"dv": "Dhivehi",
"din": "Dinka",
"doi": "Dogri",
"dov": "Dombe",
"nl": "Dutch",
"dyu": "Dyula",
"dz": "Dzongkha",
"en": "English",
"eo": "Esperanto",
"et": "Estonian",
"ee": "Ewe",
"fo": "Faroese",
"fj": "Fijian",
"tl": "Filipino",
"fi": "Finnish",
"fon": "Fon",
"fr": "French",
"fr-CA": "French (Canada)",
"fy": "Frisian",
"fur": "Friulian",
"ff": "Fulani",
"gaa": "Ga",
"gl": "Galician",
"ka": "Georgian",
"de": "German",
"el": "Greek",
"gn": "Guarani",
"gu": "Gujarati",
"ht": "Haitian Creole",
"cnh": "Hakha Chin",
"ha": "Hausa",
"haw": "Hawaiian",
"iw": "Hebrew",
"hil": "Hiligaynon",
"hi": "Hindi",
"hmn": "Hmong",
"hu": "Hungarian",
"hrx": "Hunsrik",
"iba": "Iban",
"is": "Icelandic",
"ig": "Igbo",
"ilo": "Ilocano",
"id": "Indonesian",
"iu-Latn": "Inuktut (Latin)",
"iu": "Inuktut (Syllabics)",
"ga": "Irish",
"it": "Italian",
"jam": "Jamaican Patois",
"ja": "Japanese",
"jw": "Javanese",
"kac": "Jingpo",
"kl": "Kalaallisut",
"kn": "Kannada",
"kr": "Kanuri",
"pam": "Kapampangan",
"kk": "Kazakh",
"kha": "Khasi",
"km": "Khmer",
"cgg": "Kiga",
"kg": "Kikongo",
"rw": "Kinyarwanda",
"ktu": "Kituba",
"trp": "Kokborok",
"kv": "Komi",
"gom": "Konkani",
"ko": "Korean",
"kri": "Krio",
"ku": "Kurdish (Kurmanji)",
"ckb": "Kurdish (Sorani)",
"ky": "Kyrgyz",
"lo": "Lao",
"ltg": "Latgalian",
"la": "Latin",
"lv": "Latvian",
"lij": "Ligurian",
"li": "Limburgish",
"ln": "Lingala",
"lt": "Lithuanian",
"lmo": "Lombard",
"lg": "Luganda",
"luo": "Luo",
"lb": "Luxembourgish",
"mk": "Macedonian",
"mad": "Madurese",
"mai": "Maithili",
"mak": "Makassar",
"mg": "Malagasy",
"ms": "Malay",
"ms-Arab": "Malay (Jawi)",
"ml": "Malayalam",
"mt": "Maltese",
"mam": "Mam",
"gv": "Manx",
"mi": "Maori",
"mr": "Marathi",
"mh": "Marshallese",
"mwr": "Marwadi",
"mfe": "Mauritian Creole",
"chm": "Meadow Mari",
"mni-Mtei": "Meiteilon (Manipuri)",
"min": "Minang",
"lus": "Mizo",
"mn": "Mongolian",
"my": "Myanmar (Burmese)",
"nhe": "Nahuatl (Eastern Huasteca)",
"ndc-ZW": "Ndau",
"nr": "Ndebele (South)",
"new": "Nepalbhasa (Newari)",
"ne": "Nepali",
"bm-Nkoo": "NKo",
"no": "Norwegian",
"nus": "Nuer",
"oc": "Occitan",
"or": "Odia (Oriya)",
"om": "Oromo",
"os": "Ossetian",
"pag": "Pangasinan",
"pap": "Papiamento",
"ps": "Pashto",
"fa": "Persian",
"pl": "Polish",
"pt": "Portuguese (Brazil)",
"pt-PT": "Portuguese (Portugal)",
"pa": "Punjabi (Gurmukhi)",
"pa-Arab": "Punjabi (Shahmukhi)",
"qu": "Quechua",
"kek": "Qʼeqchiʼ",
"rom": "Romani",
"ro": "Romanian",
"rn": "Rundi",
"ru": "Russian",
"se": "Sami (North)",
"sm": "Samoan",
"sg": "Sango",
"sa": "Sanskrit",
"sat-Latn": "Santali (Latin)",
"sat": "Santali (Ol Chiki)",
"gd": "Scots Gaelic",
"nso": "Sepedi",
"sr": "Serbian",
"st": "Sesotho",
"crs": "Seychellois Creole",
"shn": "Shan",
"sn": "Shona",
"scn": "Sicilian",
"szl": "Silesian",
"sd": "Sindhi",
"si": "Sinhala",
"sk": "Slovak",
"sl": "Slovenian",
"so": "Somali",
"es": "Spanish",
"su": "Sundanese",
"sus": "Susu",
"sw": "Swahili",
"ss": "Swati",
"sv": "Swedish",
"ty": "Tahitian",
"tg": "Tajik",
"ber-Latn": "Tamazight",
"ber": "Tamazight (Tifinagh)",
"ta": "Tamil",
"tt": "Tatar",
"te": "Telugu",
"tet": "Tetum",
"th": "Thai",
"bo": "Tibetan",
"ti": "Tigrinya",
"tiv": "Tiv",
"tpi": "Tok Pisin",
"to": "Tongan",
"lua": "Tshiluba",
"ts": "Tsonga",
"tn": "Tswana",
"tcy": "Tulu",
"tum": "Tumbuka",
"tr": "Turkish",
"tk": "Turkmen",
"tyv": "Tuvan",
"ak": "Twi",
"udm": "Udmurt",
"uk": "Ukrainian",
"ur": "Urdu",
"ug": "Uyghur",
"uz": "Uzbek",
"ve": "Venda",
"vec": "Venetian",
"vi": "Vietnamese",
"war": "Waray",
"cy": "Welsh",
"wo": "Wolof",
"xh": "Xhosa",
"sah": "Yakut",
"yi": "Yiddish",
"yo": "Yoruba",
"yua": "Yucatec Maya",
"zap": "Zapotec",
"zu": "Zulu"
},
"al": {}
}

426
src/locales/en.po wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -10,7 +10,7 @@ import Icon from '../components/icon';
import LangSelector from '../components/lang-selector';
import Link from '../components/link';
import RelativeTime from '../components/relative-time';
import targetLanguages from '../data/lingva-target-languages';
import languages from '../data/translang-languages';
import { api } from '../utils/api';
import getTranslateTargetLanguage from '../utils/get-translate-target-language';
import localeCode2Text from '../utils/localeCode2Text';
@ -32,10 +32,18 @@ const TEXT_SIZES = [14, 15, 16, 17, 18, 19, 20];
const {
PHANPY_WEBSITE: WEBSITE,
PHANPY_PRIVACY_POLICY_URL: PRIVACY_POLICY_URL,
PHANPY_TRANSLANG_INSTANCES: TRANSLANG_INSTANCES,
PHANPY_IMG_ALT_API_URL: IMG_ALT_API_URL,
PHANPY_GIPHY_API_KEY: GIPHY_API_KEY,
} = import.meta.env;
const targetLanguages = Object.entries(languages.tl).map(([code, name]) => ({
code,
name,
}));
const TRANSLATION_API_NAME = 'TransLang API';
function Settings({ onClose }) {
const { t } = useLingui();
const snapStates = useSnapshot(states);
@ -363,46 +371,77 @@ function Settings({ onClose }) {
<Trans>Boosts carousel</Trans>
</label>
</li>
<li class="block">
<label>
<input
type="checkbox"
checked={snapStates.settings.contentTranslation}
onChange={(e) => {
const { checked } = e.target;
states.settings.contentTranslation = checked;
if (!checked) {
states.settings.contentTranslationTargetLanguage = null;
}
}}
/>{' '}
<Trans>Post translation</Trans>
</label>
<div
class={`sub-section ${
!snapStates.settings.contentTranslation
? 'more-insignificant'
: ''
}`}
>
<div>
<label>
<Trans>Translate to </Trans>{' '}
<select
value={targetLanguage || ''}
disabled={!snapStates.settings.contentTranslation}
style={{ width: '10em' }}
onChange={(e) => {
states.settings.contentTranslationTargetLanguage =
e.target.value || null;
}}
>
<option value="">
<Trans>
System language ({systemTargetLanguageText})
</Trans>
</option>
<option disabled></option>
{!!TRANSLANG_INSTANCES && (
<li class="block">
<label>
<input
type="checkbox"
checked={snapStates.settings.contentTranslation}
onChange={(e) => {
const { checked } = e.target;
states.settings.contentTranslation = checked;
if (!checked) {
states.settings.contentTranslationTargetLanguage = null;
}
}}
/>{' '}
<Trans>Post translation</Trans>
</label>
<div
class={`sub-section ${
!snapStates.settings.contentTranslation
? 'more-insignificant'
: ''
}`}
>
<div>
<label>
<Trans>Translate to </Trans>{' '}
<select
value={targetLanguage || ''}
disabled={!snapStates.settings.contentTranslation}
style={{ width: '10em' }}
onChange={(e) => {
states.settings.contentTranslationTargetLanguage =
e.target.value || null;
}}
>
<option value="">
<Trans>
System language ({systemTargetLanguageText})
</Trans>
</option>
<option disabled></option>
{targetLanguages.map((lang) => {
const common = localeCode2Text({
code: lang.code,
fallback: lang.name,
});
const native = localeCode2Text({
code: lang.code,
locale: lang.code,
});
const showCommon = native && common !== native;
return (
<option value={lang.code}>
{showCommon ? `${native} - ${common}` : common}
</option>
);
})}
</select>
</label>
</div>
<hr />
<div class="checkbox-fieldset">
<Plural
value={
snapStates.settings.contentTranslationHideLanguages
.length
}
_0={`Hide "Translate" button for:`}
other={`Hide "Translate" button for (#):`}
/>
<div class="checkbox-fields">
{targetLanguages.map((lang) => {
const common = localeCode2Text({
code: lang.code,
@ -412,120 +451,86 @@ function Settings({ onClose }) {
code: lang.code,
locale: lang.code,
});
const showCommon = common !== native;
const showCommon = native && common !== native;
return (
<option value={lang.code}>
{showCommon ? `${native} - ${common}` : common}
</option>
<label>
<input
type="checkbox"
checked={snapStates.settings.contentTranslationHideLanguages.includes(
lang.code,
)}
onChange={(e) => {
const { checked } = e.target;
if (checked) {
states.settings.contentTranslationHideLanguages.push(
lang.code,
);
} else {
states.settings.contentTranslationHideLanguages =
snapStates.settings.contentTranslationHideLanguages.filter(
(code) => code !== lang.code,
);
}
}}
/>{' '}
{showCommon ? (
<span>
{native}{' '}
<span class="insignificant ib">- {common}</span>
</span>
) : (
common
)}
</label>
);
})}
</select>
</label>
</div>
<hr />
<div class="checkbox-fieldset">
<Plural
value={
snapStates.settings.contentTranslationHideLanguages.length
}
_0={`Hide "Translate" button for:`}
other={`Hide "Translate" button for (#):`}
/>
<div class="checkbox-fields">
{targetLanguages.map((lang) => {
const common = localeCode2Text({
code: lang.code,
fallback: lang.name,
});
const native = localeCode2Text({
code: lang.code,
locale: lang.code,
});
const showCommon = common !== native;
return (
<label>
<input
type="checkbox"
checked={snapStates.settings.contentTranslationHideLanguages.includes(
lang.code,
)}
onChange={(e) => {
const { checked } = e.target;
if (checked) {
states.settings.contentTranslationHideLanguages.push(
lang.code,
);
} else {
states.settings.contentTranslationHideLanguages =
snapStates.settings.contentTranslationHideLanguages.filter(
(code) => code !== lang.code,
);
}
}}
/>{' '}
{showCommon ? (
<span>
{native}{' '}
<span class="insignificant">- {common}</span>
</span>
) : (
common
)}
</label>
);
})}
</div>
</div>
</div>
<p class="insignificant">
<small>
<Trans>
Note: This feature uses external translation services,
powered by{' '}
<a
href="https://github.com/cheeaun/lingva-api"
target="_blank"
rel="noopener"
>
Lingva API
</a>{' '}
&amp;{' '}
<a
href="https://github.com/thedaviddelta/lingva-translate"
target="_blank"
rel="noopener"
>
Lingva Translate
</a>
.
</Trans>
</small>
</p>
<hr />
<div>
<label>
<input
type="checkbox"
checked={snapStates.settings.contentTranslationAutoInline}
disabled={!snapStates.settings.contentTranslation}
onChange={(e) => {
states.settings.contentTranslationAutoInline =
e.target.checked;
}}
/>{' '}
<Trans>Auto inline translation</Trans>
</label>
<p class="insignificant">
<small>
<Trans>
Automatically show translation for posts in timeline.
Only works for <b>short</b> posts without content
warning, media and poll.
Note: This feature uses external translation services,
powered by{' '}
<a
href="https://github.com/cheeaun/translang-api"
target="_blank"
rel="noopener"
>
{TRANSLATION_API_NAME}
</a>
.
</Trans>
</small>
</p>
<hr />
<div>
<label>
<input
type="checkbox"
checked={
snapStates.settings.contentTranslationAutoInline
}
disabled={!snapStates.settings.contentTranslation}
onChange={(e) => {
states.settings.contentTranslationAutoInline =
e.target.checked;
}}
/>{' '}
<Trans>Auto inline translation</Trans>
</label>
<p class="insignificant">
<small>
<Trans>
Automatically show translation for posts in timeline.
Only works for <b>short</b> posts without content
warning, media and poll.
</Trans>
</small>
</p>
</div>
</div>
</div>
</li>
</li>
)}
{!!GIPHY_API_KEY && authenticated && (
<li class="block">
<label>

Wyświetl plik

@ -1,9 +1,16 @@
import translationTargetLanguages from '../data/lingva-target-languages';
import languages from '../data/translang-languages';
import localeMatch from './locale-match';
import mem from './mem';
import states from './states';
const translationTargetLanguages = Object.entries(languages).map(
([code, { name }]) => ({
code,
name,
}),
);
const locales = mem(() => [
new Intl.DateTimeFormat().resolvedOptions().locale,
...navigator.languages,

Wyświetl plik

@ -1,5 +1,7 @@
import { i18n } from '@lingui/core';
import translangLanguagesNative from '../data/translang-languages-native';
import mem from './mem';
// Some codes are not supported by Intl.DisplayNames
@ -28,6 +30,8 @@ function _localeCode2Text(code) {
if (!fallback) {
const anotherText = IntlDN(code).of(code);
if (anotherText !== code) return anotherText;
const yetAnotherText = translangLanguagesNative?.[locale];
if (yetAnotherText !== code) return yetAnotherText;
}
return fallback || '';
} catch (e) {