mirror of https://github.com/VERT-sh/VERT.git
Compare commits
12 Commits
3f64a5d095
...
d89a343eed
Author | SHA1 | Date |
---|---|---|
|
d89a343eed | |
|
92fa929d2a | |
|
704e693511 | |
|
8d38007461 | |
|
17e024d763 | |
|
e2221e460a | |
|
97ecf9e520 | |
|
45b9158140 | |
|
b9d94a3f82 | |
|
44a96313b9 | |
|
35929d824b | |
|
65ffb1a3c6 |
|
@ -79,6 +79,11 @@
|
|||
"errors": {
|
||||
"cant_convert": "We can't convert this file.",
|
||||
"vertd_server": "what are you doing..? you're supposed to run the vertd server!",
|
||||
"vertd_generic_body": "An error occurred whilst whilst trying convert your video. Would you like to submit this video to the developers to help fix this bug? Only your video file will be sent. No identifiers will be uploaded.",
|
||||
"vertd_generic_title": "Video conversion error",
|
||||
"vertd_generic_yes": "Submit video",
|
||||
"vertd_generic_no": "Don't submit",
|
||||
"vertd_failed_to_keep": "Failed to keep the video on the server: {error}",
|
||||
"unsupported_format": "Only image, video, audio, and document files are supported",
|
||||
"vertd_not_found": "Could not find the vertd instance to start video conversion. Are you sure the instance URL is set correctly?",
|
||||
"worker_downloading": "The {type} converter is currently being initialized, please wait a few moments.",
|
||||
|
@ -167,7 +172,8 @@
|
|||
"loading_cache": "Loading...",
|
||||
"total_size": "Total Size",
|
||||
"files_cached_label": "Files Cached",
|
||||
"cache_cleared": "Cache cleared successfully!"
|
||||
"cache_cleared": "Cache cleared successfully!",
|
||||
"cache_clear_error": "Failed to clear cache."
|
||||
},
|
||||
"language": {
|
||||
"title": "Language",
|
||||
|
|
|
@ -31,7 +31,11 @@
|
|||
"status": {
|
||||
"text": "<b>Estado:</b> {status}",
|
||||
"ready": "listo",
|
||||
"not_ready": "no listo"
|
||||
"not_ready": "no listo",
|
||||
"not_initialized": "no inicializado",
|
||||
"downloading": "descargando...",
|
||||
"initializing": "inicializando...",
|
||||
"unknown": "estado desconocido"
|
||||
},
|
||||
"supported_formats": "Formatos soportados:"
|
||||
},
|
||||
|
@ -43,6 +47,12 @@
|
|||
}
|
||||
},
|
||||
"convert": {
|
||||
"external_warning": {
|
||||
"title": "Advertencia del servidor externo",
|
||||
"text": "Si eliges convertir a un formato de video, esos archivos se cargarán en un servidor externo para convertirlos. ¿Quieres continuar?",
|
||||
"yes": "Sí",
|
||||
"no": "No"
|
||||
},
|
||||
"panel": {
|
||||
"convert_all": "Convertir todo",
|
||||
"download_all": "Comprimir todo",
|
||||
|
@ -70,7 +80,13 @@
|
|||
"cant_convert": "No podemos convertir este archivo.",
|
||||
"vertd_server": "¿Qué estás haciendo..? ¡Debes ejecutar el servidor de vertd!",
|
||||
"unsupported_format": "Solo aceptamos imágenes, vídeos, audios y documentos.",
|
||||
"vertd_not_found": "No se encontró la instancia de vertd para iniciar la conversión de vídeos. ¿Estás seguro de que la URL es correcta?"
|
||||
"vertd_not_found": "No se encontró la instancia de vertd para iniciar la conversión de vídeos. ¿Estás seguro de que la URL es correcta?",
|
||||
"worker_downloading": "El convertidor {type} se está inicializando actualmente, espere unos momentos.",
|
||||
"worker_error": "El convertidor {type} tuvo un error durante la inicialización, inténtelo nuevamente más tarde.",
|
||||
"worker_timeout": "El convertidor {type} está tardando más de lo esperado en inicializarse. Espere unos momentos más o actualice la página.",
|
||||
"audio": "audio",
|
||||
"doc": "documento",
|
||||
"image": "imagen"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
|
@ -91,9 +107,20 @@
|
|||
},
|
||||
"conversion": {
|
||||
"title": "Conversión",
|
||||
"advanced_settings": "Configuraciones avanzadas",
|
||||
"filename_format": "Formato del nombre de archivo",
|
||||
"filename_description": "Esto va a determinar el nombre del archivo al ser descargado <b>sin incluir la extensión</b>. Puedes poner las siguientes plantillas en el formato, las cuales serán reemplazadas con la información que les corresponde: <b>%name%</b> para el nombre original, <b>%extension%</b> para la extensión original del archivo y <b>%date%</b> para la fecha de cuando el archivo fue convertido.",
|
||||
"placeholder": "VERT_%name%",
|
||||
"default_format": "Formato de conversión predeterminado",
|
||||
"default_format_description": "Esto cambiará el formato predeterminado seleccionado cuando subes un archivo de este tipo.",
|
||||
"default_format_image": "Imágenes",
|
||||
"default_format_video": "Vídeos",
|
||||
"default_format_audio": "Audio",
|
||||
"default_format_document": "Documentos",
|
||||
"metadata": "Metadatos del archivo",
|
||||
"metadata_description": "Esto cambia si los metadatos (EXIF, información de la canción, etc.) del archivo original se conservan en los archivos convertidos.",
|
||||
"keep": "Mantener",
|
||||
"remove": "Eliminar",
|
||||
"quality": "Calidad de la conversión",
|
||||
"quality_description": "Esto cambia la calidad por defecto de los archivos convertidos (en su categoría). Valores más altos pueden resultar en tiempos de conversión y tamaños de archivo más largos.",
|
||||
"quality_video": "Esto cambia la calidad por defecto de los vídeos convertidos. Valores más altos pueden resultar en tiempos de conversión y tamaños de archivo más largos.",
|
||||
|
@ -120,14 +147,27 @@
|
|||
"medium": "Medio",
|
||||
"fast": "Rápido",
|
||||
"ultra_fast": "Súper rápido"
|
||||
}
|
||||
},
|
||||
"auto_instance": "Automático (recomendado)",
|
||||
"eu_instance": "Falkenstein, Alemania",
|
||||
"us_instance": "Washington, EE. UU.",
|
||||
"custom_instance": "Personalizado"
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Privacidad",
|
||||
"plausible_title": "Analíticas de Plausible",
|
||||
"plausible_description": "Usamos [plausible_link]Plausible[/plausible_link], una herramienta de analíticas orientada a la privacidad para recopilar estadísticas completamente anónimas. Toda la información que recopilamos es anonimizada y agregada, y en ningún momento se envía ni se almacena información que permita identificarte. Puedes ver las estadísticas [analytics_link]aquí[/analytics_link] y excluirte de ellas a continuación:",
|
||||
"opt_in": "Participar",
|
||||
"opt_out": "No participar"
|
||||
"opt_out": "No participar",
|
||||
"cache_title": "Administración de caché",
|
||||
"cache_description": "Guardamos en caché los archivos del convertidor en su navegador para que no tenga que volver a descargarlos cada vez, mejorando el rendimiento y reduciendo el uso de datos.",
|
||||
"refresh_cache": "Actualizar caché",
|
||||
"clear_cache": "Borrar caché",
|
||||
"files_cached": "{size} ({count} archivos)",
|
||||
"loading_cache": "Cargando...",
|
||||
"total_size": "Tamaño total",
|
||||
"files_cached_label": "Archivos en caché",
|
||||
"cache_cleared": "¡Caché borrada exitosamente!"
|
||||
},
|
||||
"language": {
|
||||
"title": "Lenguaje",
|
||||
|
@ -189,6 +229,7 @@
|
|||
"workers": {
|
||||
"errors": {
|
||||
"general": "Ocurrió un error mientras se convertía {file}: {message}",
|
||||
"cancel": "Error al cancelar la conversión para {file}: {message}",
|
||||
"magick": "Ocurrió un error en el módulo de Magick, la conversión de imágenes puede que no funcione correctamente.",
|
||||
"ffmpeg": "No se pudo cargar FFmpeg, algunas funciones podrían no funcionar.",
|
||||
"no_audio": "No se encontró una pista de audio.",
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"navbar": {
|
||||
"upload": "アップロード",
|
||||
"convert": "変換",
|
||||
"settings": "設定",
|
||||
"about": "について",
|
||||
"toggle_theme": "テーマを切り替える"
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "© {year} VERT.",
|
||||
"source_code": "ソースコード",
|
||||
"discord_server": "Discordサーバー"
|
||||
},
|
||||
"upload": {
|
||||
"title": "きっと気に入るファイル変換ツール。",
|
||||
"subtitle": "すべての画像・音声・ドキュメント処理はデバイス上で行われます。動画は超高速サーバーで変換されます。ファイルサイズ制限なし、広告なし、完全オープンソース。",
|
||||
"uploader": {
|
||||
"text": "ドロップまたはクリックして{action}",
|
||||
"convert": "変換",
|
||||
"jpegify": "JPEG化"
|
||||
},
|
||||
"cards": {
|
||||
"title": "VERTがサポートしている形式",
|
||||
"images": "画像",
|
||||
"audio": "音声",
|
||||
"documents": "ドキュメント",
|
||||
"video": "動画",
|
||||
"video_server_processing": "サーバー対応",
|
||||
"local_supported": "ローカル対応",
|
||||
"status": {
|
||||
"text": "<b>ステータス:</b> {status}",
|
||||
"ready": "準備完了",
|
||||
"not_ready": "未準備",
|
||||
"not_initialized": "未初期化",
|
||||
"downloading": "ダウンロード中...",
|
||||
"initializing": "初期化中...",
|
||||
"unknown": "不明なステータス"
|
||||
},
|
||||
"supported_formats": "対応フォーマット:"
|
||||
},
|
||||
"tooltip": {
|
||||
"partial_support": "このフォーマットは{direction}としてのみ変換可能です。",
|
||||
"direction_input": "入力(変換元)",
|
||||
"direction_output": "出力(変換先)",
|
||||
"video_server_processing": "動画はデフォルトでサーバーにアップロードされて処理されます。ローカルで設定する方法はこちら。"
|
||||
}
|
||||
},
|
||||
"convert": {
|
||||
"external_warning": {
|
||||
"title": "外部サーバーの警告",
|
||||
"text": "動画フォーマットへの変換を選択すると、ファイルは外部サーバーにアップロードされて変換されます。続行しますか?",
|
||||
"yes": "はい",
|
||||
"no": "いいえ"
|
||||
},
|
||||
"panel": {
|
||||
"convert_all": "すべて変換",
|
||||
"download_all": "すべてを.zipでダウンロード",
|
||||
"remove_all": "すべてのファイルを削除",
|
||||
"set_all_to": "すべてを設定",
|
||||
"na": "該当なし"
|
||||
},
|
||||
"dropdown": {
|
||||
"audio": "音声",
|
||||
"video": "動画",
|
||||
"doc": "ドキュメント",
|
||||
"image": "画像",
|
||||
"placeholder": "フォーマットを検索"
|
||||
},
|
||||
"tooltips": {
|
||||
"unknown_file": "不明なファイルタイプ",
|
||||
"audio_file": "音声ファイル",
|
||||
"video_file": "動画ファイル",
|
||||
"document_file": "ドキュメントファイル",
|
||||
"image_file": "画像ファイル",
|
||||
"convert_file": "このファイルを変換",
|
||||
"download_file": "このファイルをダウンロード"
|
||||
},
|
||||
"errors": {
|
||||
"cant_convert": "このファイルを変換できません。",
|
||||
"vertd_server": "何してるの..? vertdサーバーを起動する必要があります!",
|
||||
"unsupported_format": "画像、動画、音声、ドキュメントのみ対応しています",
|
||||
"vertd_not_found": "動画変換を開始するためのvertdインスタンスが見つかりません。URLが正しいか確認してください。",
|
||||
"worker_downloading": "{type}コンバーターを初期化中です。少々お待ちください。",
|
||||
"worker_error": "{type}コンバーターの初期化中にエラーが発生しました。後でもう一度お試しください。",
|
||||
"worker_timeout": "{type}コンバーターの初期化に予想以上の時間がかかっています。もう少しお待ちいただくか、ページを更新してください。",
|
||||
"audio": "音声",
|
||||
"doc": "ドキュメント",
|
||||
"image": "画像"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "設定",
|
||||
"errors": {
|
||||
"save_failed": "設定の保存に失敗しました!"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "外観",
|
||||
"brightness_theme": "明るさテーマ",
|
||||
"brightness_description": "まぶしい昼間か、静かな夜か?",
|
||||
"light": "ライト",
|
||||
"dark": "ダーク",
|
||||
"effect_settings": "エフェクト設定",
|
||||
"effect_description": "派手な効果にしますか?それとも静的な体験にしますか?",
|
||||
"enable": "有効",
|
||||
"disable": "無効"
|
||||
},
|
||||
"conversion": {
|
||||
"title": "変換",
|
||||
"advanced_settings": "詳細設定",
|
||||
"filename_format": "ファイル名フォーマット",
|
||||
"filename_description": "これはダウンロード時のファイル名を決定します(拡張子を除く)。以下のテンプレートを使用できます:<b>%name%</b>(元のファイル名)、<b>%extension%</b>(元の拡張子)、<b>%date%</b>(変換日時)。",
|
||||
"placeholder": "VERT_%name%",
|
||||
"default_format": "デフォルト変換フォーマット",
|
||||
"default_format_description": "このファイルタイプをアップロードしたときに自動で選択される形式を変更します。",
|
||||
"default_format_image": "画像",
|
||||
"default_format_video": "動画",
|
||||
"default_format_audio": "音声",
|
||||
"default_format_document": "ドキュメント",
|
||||
"metadata": "ファイルメタデータ",
|
||||
"metadata_description": "変換後のファイルに元のメタデータ(EXIF、曲情報など)を保持するかどうかを変更します。",
|
||||
"keep": "保持",
|
||||
"remove": "削除",
|
||||
"quality": "変換品質",
|
||||
"quality_description": "出力ファイルの品質を変更します。値が高いほど処理時間とファイルサイズが増加します。",
|
||||
"quality_video": "動画変換の品質を変更します。高品質ほど変換時間とサイズが増加します。",
|
||||
"quality_audio": "音声(kbps)",
|
||||
"quality_images": "画像(%)",
|
||||
"rate": "サンプリングレート(Hz)"
|
||||
},
|
||||
"vertd": {
|
||||
"title": "動画変換",
|
||||
"status": "ステータス:",
|
||||
"loading": "読み込み中...",
|
||||
"available": "利用可能(コミットID {commitId})",
|
||||
"unavailable": "利用不可(URLが正しいですか?)",
|
||||
"description": "<code>vertd</code>プロジェクトはFFmpegのサーバーラッパーです。これにより、GPUの性能を活かして高速に変換しつつ、VERTのウェブインターフェイスから簡単に動画を変換できます。",
|
||||
"hosting_info": "私たちは利便性のために公開インスタンスをホストしていますが、自分のPCやサーバーでも簡単にホストできます。バイナリは[vertd_link]こちら[/vertd_link]からダウンロードできます。今後さらにセットアップが簡単になる予定です!",
|
||||
"instance": "インスタンス",
|
||||
"url_placeholder": "例: http://localhost:24153",
|
||||
"conversion_speed": "変換速度",
|
||||
"speed_description": "速度と品質のバランスを設定します。高速化すると品質が低下しますが、処理は速くなります。",
|
||||
"speeds": {
|
||||
"very_slow": "非常に遅い",
|
||||
"slower": "かなり遅い",
|
||||
"slow": "遅い",
|
||||
"medium": "普通",
|
||||
"fast": "速い",
|
||||
"ultra_fast": "超高速"
|
||||
},
|
||||
"auto_instance": "自動(推奨)",
|
||||
"eu_instance": "ドイツ・ファルケンシュタイン",
|
||||
"us_instance": "アメリカ・ワシントン",
|
||||
"custom_instance": "カスタム"
|
||||
},
|
||||
"privacy": {
|
||||
"title": "プライバシーとデータ",
|
||||
"plausible_title": "Plausible解析",
|
||||
"plausible_description": "私たちはプライバシー重視の解析ツール[plausible_link]Plausible[/plausible_link]を使用しています。すべてのデータは匿名化・集計され、個人情報は一切収集・保存されません。統計情報は[analytics_link]こちら[/analytics_link]で確認でき、以下でオプトアウト可能です。",
|
||||
"opt_in": "参加する",
|
||||
"opt_out": "参加しない",
|
||||
"cache_title": "キャッシュ管理",
|
||||
"cache_description": "コンバーターファイルをブラウザにキャッシュして再ダウンロードを防ぎ、パフォーマンスを向上させます。",
|
||||
"refresh_cache": "キャッシュを更新",
|
||||
"clear_cache": "キャッシュをクリア",
|
||||
"files_cached": "{size}({count}ファイル)",
|
||||
"loading_cache": "読み込み中...",
|
||||
"total_size": "合計サイズ",
|
||||
"files_cached_label": "キャッシュ済みファイル",
|
||||
"cache_cleared": "キャッシュが正常にクリアされました!"
|
||||
},
|
||||
"language": {
|
||||
"title": "言語",
|
||||
"description": "VERTインターフェイスの表示言語を選択してください。"
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "について",
|
||||
"why": {
|
||||
"title": "なぜVERT?",
|
||||
"description": "<b>従来のファイルコンバーターにはいつもがっかりしてきました。</b>見た目が悪く、広告だらけで、そして何より遅い。私たちはそれらの問題をすべて解決するためにVERTを作りました。<br/><br/>動画以外のファイルは完全にデバイス上で変換されるため、サーバーとのやり取りによる遅延もなく、あなたのファイルを覗き見ることもありません。<br/><br/>動画は超高速RTX 4000 Adaサーバーで処理され、変換しなかった場合は1時間以内に削除されます。変換された動画も1時間またはダウンロード完了後に削除されます。"
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "スポンサー",
|
||||
"description": "私たちを支援したい場合は、[discord_link]Discord[/discord_link]サーバーで開発者に連絡するか、以下のメールアドレスまでご連絡ください。",
|
||||
"email_copied": "メールアドレスをコピーしました!"
|
||||
},
|
||||
"resources": {
|
||||
"title": "リソース",
|
||||
"discord": "Discord",
|
||||
"source": "ソース",
|
||||
"email": "メール"
|
||||
},
|
||||
"donate": {
|
||||
"title": "VERTを支援する",
|
||||
"description": "あなたの支援でVERTの維持と改善を続けられます。",
|
||||
"one_time": "一度きり",
|
||||
"monthly": "毎月",
|
||||
"custom": "カスタム",
|
||||
"pay_now": "今すぐ支払う",
|
||||
"donate_amount": "${amount} USDを寄付",
|
||||
"thank_you": "ご支援ありがとうございます!",
|
||||
"payment_failed": "支払いに失敗しました: {message}{period} 請求は行われていません。",
|
||||
"donation_error": "寄付の処理中にエラーが発生しました。後でもう一度お試しください。",
|
||||
"payment_error": "支払い情報の取得中にエラーが発生しました。後でもう一度お試しください。"
|
||||
},
|
||||
"credits": {
|
||||
"title": "クレジット",
|
||||
"contact_team": "開発チームに連絡したい場合は、「リソース」カードに記載されたメールをご利用ください。",
|
||||
"notable_contributors": "特筆すべき貢献者",
|
||||
"notable_description": "VERTに大きく貢献してくださった方々に感謝します。",
|
||||
"github_contributors": "GitHubの貢献者",
|
||||
"github_description": "多くの方々に[jpegify_link]感謝[/jpegify_link]します![github_link]あなたも参加してみませんか?[/github_link]",
|
||||
"no_contributors": "まだ誰も貢献していないようです… [contribute_link]最初の貢献者になりましょう![/contribute_link]",
|
||||
"libraries": "ライブラリ",
|
||||
"libraries_description": "長年にわたり優れたライブラリを提供してくれているFFmpeg(音声・動画)、ImageMagick(画像)、Pandoc(ドキュメント)に感謝します。VERTはこれらに依存して動作しています。",
|
||||
"roles": {
|
||||
"lead_developer": "リード開発者;変換バックエンド、UI実装",
|
||||
"developer": "開発者;UI実装",
|
||||
"designer": "デザイナー;UX、ブランディング、マーケティング",
|
||||
"docker_ci": "DockerとCIの保守担当",
|
||||
"former_cofounder": "元共同創設者・デザイナー"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"github_contributors": "GitHub貢献者の取得エラー"
|
||||
}
|
||||
},
|
||||
"workers": {
|
||||
"errors": {
|
||||
"general": "{file}の変換エラー:{message}",
|
||||
"cancel": "{file}の変換キャンセルエラー:{message}",
|
||||
"magick": "Magickワーカーでエラーが発生しました。画像変換が正常に動作しない可能性があります。",
|
||||
"ffmpeg": "ffmpegの読み込みエラー。一部の機能が動作しない可能性があります。",
|
||||
"no_audio": "音声ストリームが見つかりません。",
|
||||
"invalid_rate": "無効なサンプリングレートが指定されました: {rate}Hz"
|
||||
}
|
||||
},
|
||||
"jpegify": {
|
||||
"title": "秘密のJPEGIFY!!!",
|
||||
"subtitle": "(しっ…誰にも言わないで!)",
|
||||
"button": "JPEGIFY {compression}%!!!",
|
||||
"download": "ダウンロード",
|
||||
"delete": "削除"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||
"navbar": {
|
||||
"upload": "Yükle",
|
||||
"convert": "Dönüştür",
|
||||
"settings": "Ayarlar",
|
||||
"about": "Hakkımızda",
|
||||
"toggle_theme": "Temayı değiştir"
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "© {year} VERT.",
|
||||
"source_code": "Kaynak kodu",
|
||||
"discord_server": "Discord sunucusu"
|
||||
},
|
||||
"upload": {
|
||||
"title": "Sevdiğiniz dosya dönüştürücü.",
|
||||
"subtitle": "Tüm görüntü, ses ve belge işlemleri cihazınızda gerçekleştirilir. Videolar, ışık hızındaki sunucularımızda dönüştürülür. Dosya boyutu sınırı ve reklam yoktur. Tamamen açık kaynaklıdır.",
|
||||
"uploader": {
|
||||
"text": "{action} için sürükleyip bırakın veya dosya seçin",
|
||||
"convert": "dönüştür",
|
||||
"jpegify": "jpegify"
|
||||
},
|
||||
"cards": {
|
||||
"title": "VERT'in desteklediği formatlar...",
|
||||
"images": "Görsel",
|
||||
"audio": "Ses",
|
||||
"documents": "Belge",
|
||||
"video": "Video",
|
||||
"video_server_processing": "Sunucuda gerçekleşir",
|
||||
"local_supported": "Lokalde gerçekleşir",
|
||||
"status": {
|
||||
"text": "<b>Durum:</b> {status}",
|
||||
"ready": "hazır",
|
||||
"not_ready": "hazır değil",
|
||||
"not_initialized": "başlatılmamış",
|
||||
"downloading": "indiriliyor...",
|
||||
"initializing": "başlatılıyor...",
|
||||
"unknown": "bilinmeyen durum"
|
||||
},
|
||||
"supported_formats": "Desteklenen formatlar:"
|
||||
},
|
||||
"tooltip": {
|
||||
"partial_support": "Bu format yalnızca şu şekilde dönüştürülebilir: {direction}.",
|
||||
"direction_input": "kaynak",
|
||||
"direction_output": "çıktı",
|
||||
"video_server_processing": "Videolar varsayılan olarak işlenmek üzere sunucuya yüklenir. Yerel olarak nasıl ayarlayacağınızı buradan öğrenebilirsiniz."
|
||||
}
|
||||
},
|
||||
"convert": {
|
||||
"external_warning": {
|
||||
"title": "Harici sunucu uyarısı",
|
||||
"text": "Video formatına dönüştürmeyi seçerseniz, bu dosyalar dönüştürülmek üzere harici bir sunucuya yüklenecektir. Devam etmek istiyor musunuz?",
|
||||
"yes": "Evet",
|
||||
"no": "Hayır"
|
||||
},
|
||||
"panel": {
|
||||
"convert_all": "Tümünü dönüştür",
|
||||
"download_all": "Tümünü .zip olarak indir",
|
||||
"remove_all": "Tüm dosyaları kaldır",
|
||||
"set_all_to": "Tümünü ayarla",
|
||||
"na": "N/A"
|
||||
},
|
||||
"dropdown": {
|
||||
"audio": "Ses",
|
||||
"video": "Video",
|
||||
"doc": "Belge",
|
||||
"image": "Görsel",
|
||||
"placeholder": "Format ara"
|
||||
},
|
||||
"tooltips": {
|
||||
"unknown_file": "Bilinmeyen dosya türü",
|
||||
"audio_file": "Ses dosyası",
|
||||
"video_file": "Video dosyası",
|
||||
"document_file": "Belge dosyası",
|
||||
"image_file": "Görsel dosyası",
|
||||
"convert_file": "Bu dosyayı dönüştür",
|
||||
"download_file": "Bu dosyayı indir"
|
||||
},
|
||||
"errors": {
|
||||
"cant_convert": "Bu dosyayı dönüştüremiyoruz.",
|
||||
"vertd_server": "Ne yapıyorsun..? vertd sunucusunu çalıştırman gerekiyordu!",
|
||||
"unsupported_format": "Yalnızca görüntü, video, ses ve belge dosyaları desteklenir.",
|
||||
"vertd_not_found": "Video dönüştürme işlemini başlatmak için vertd örneği bulunamadı. Sunucu URL’sinin doğru ayarlandığından emin misiniz?",
|
||||
"worker_downloading": "{type} dönüştürme işlemi şu anda başlatılıyor, lütfen birkaç saniye bekleyin.",
|
||||
"worker_error": "{type} dönüştürme işlemi başlatılırken bir hata oluştu, lütfen daha sonra tekrar deneyin.",
|
||||
"worker_timeout": "{type} dönüştürme işlemi beklenenden daha uzun sürüyor, lütfen biraz daha bekleyin veya sayfayı yenileyin.",
|
||||
"audio": "ses",
|
||||
"doc": "belge",
|
||||
"image": "görsel"
|
||||
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ayarlar",
|
||||
"errors": {
|
||||
"save_failed": "Ayarlar kaydedilirken hata oluştu!"
|
||||
},
|
||||
"appearance": {
|
||||
"title": "Görünüm",
|
||||
"brightness_theme": "Tema seçimi",
|
||||
"brightness_description": "Güneşli bir gün mü istersiniz, yoksa sessiz ve yalnız bir gece mi?",
|
||||
"light": "Açık",
|
||||
"dark": "Koyu",
|
||||
"effect_settings": "Efekt ayarları",
|
||||
"effect_description": "Süslü efektler mi istersiniz, yoksa daha sade bir deneyim mi?",
|
||||
"enable": "Etkinleştir",
|
||||
"disable": "Devre dışı bırak"
|
||||
},
|
||||
"conversion": {
|
||||
"title": "Dönüştürme",
|
||||
"advanced_settings": "Gelişmiş ayarlar",
|
||||
"filename_format": "Dosya adı formatı",
|
||||
"filename_description": "Bu ayar, <b>dosya uzantısını etkilemeden</b>indirilen dosyanın adını belirleyecektir. Aşağıdaki şablonları formata ekleyebilirsiniz, bunlar ilgili bilgilerle değiştirilecektir: orijinal dosya adı için <b>%name%</b>, orijinal dosya uzantısı için <b>%extension%</b> ve dosyanın dönüştürüldüğü tarihin tarih için <b>%date%</b>.",
|
||||
"placeholder": "VERT_%name%",
|
||||
"default_format": "Varsayılan dönüştürme formatı",
|
||||
"default_format_description": "Bu ayar, bu dosya türünde bir dosya yüklediğinizde seçili olan varsayılan formatı değiştirecektir.",
|
||||
"default_format_image": "Görsel",
|
||||
"default_format_video": "Video",
|
||||
"default_format_audio": "Ses",
|
||||
"default_format_document": "Belge",
|
||||
"metadata": "Dosya metadata",
|
||||
"metadata_description": "Bu ayar, orijinal dosyadaki meta verilerin (EXIF, şarkı bilgileri vb.) dönüştürülen dosyalarda korunup korunmayacağını değiştirir.",
|
||||
"keep": "Sakla",
|
||||
"remove": "Kaldır",
|
||||
"quality": "Dönüştürme kalitesi",
|
||||
"quality_description": "Bu, dönüştürülen dosyaların (kendi kategorisinde) varsayılan çıktı kalitesini değiştirir. Yüksek değerler, uzun dönüştürme sürelerine ve büyük dosya boyutuna neden olabilir.",
|
||||
"quality_video": "Bu, dönüştürülen videoların varsayılan çıktı kalitesini değiştirir. Yüksek değerler, uzun dönüştürme sürelerine ve büyük dosya boyutuna neden olabilir.",
|
||||
"quality_audio": "Ses (kbps)",
|
||||
"quality_images": "Görsel (%)",
|
||||
"rate": "Örnekleme oranı (Hz)"
|
||||
},
|
||||
"vertd": {
|
||||
"title": "Video dönüştürme",
|
||||
"status": "durum:",
|
||||
"loading": "yükleniyor...",
|
||||
"available": "uygun, işlem no: {commitId}",
|
||||
"unavailable": "uygun değil (url doğru mu?)",
|
||||
"description": "<code>vertd</code> projesi, FFmpeg için bir sunucu sarmalayıcısıdır (server wrapper). Bu ayar, VERT'in web arayüzünün kullanım kolaylığı ile videoları dönüştürmenize olanak sağlarken, ekran kartınızın gücünden yararlanarak işlemi mümkün olan en hızlı şekilde yapmanızı sağlar.",
|
||||
"hosting_info": "Kolaylık sağlaması açısından herkese açık bir dönüştürücü sunuyoruz, ancak kendi bilgisayarınızda veya sunucunuzda kendi dönüştürücünüzü kurmak da oldukça kolaydır. Sunucu binary dosyalarını [vertd_link]buradan[/vertd_link] indirebilirsiniz. Kurulum işlemini gelecekte daha kolay hale getirmeye çalışıyoruz, bu nedenle bizi takip etmeyi unutmayın!",
|
||||
"instance": "Sunucu",
|
||||
"url_placeholder": "Örneğin: http://localhost:24153",
|
||||
"conversion_speed": "Dönüştürme hızı",
|
||||
"speed_description": "Bu ayar, hız ve kalite arasındaki dengeyi belirlemenizi sağlar. Yüksek hızlar, düşük kaliteye neden olur ancak işlem daha hızlı tamamlanır.",
|
||||
"speeds": {
|
||||
"very_slow": "En Yavaş",
|
||||
"slower": "Daha Yavaş",
|
||||
"slow": "Yavaş",
|
||||
"medium": "Orta",
|
||||
"fast": "Hızlı",
|
||||
"ultra_fast": "En Hızlı"
|
||||
},
|
||||
"auto_instance": "Otomatik (önerilen)",
|
||||
"eu_instance": "Falkenstein, Germany",
|
||||
"us_instance": "Washington, USA",
|
||||
"custom_instance": "Özel"
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Gizlilik & kişisel veriler",
|
||||
"plausible_title": "Plausible analytics",
|
||||
"plausible_description": "Tamamen anonim istatistikler toplamak için gizliliğe odaklı bir analiz aracı olan [plausible_link]Plausible[/plausible_link]’ı kullanıyoruz. Tüm veriler anonimleştirilmiş ve birleştirilmiş şekilde işlenir; hiçbir kişisel veya tanımlanabilir bilgi gönderilmez ya da saklanmaz. Analitik verilerini [analytics_link]buradan[/analytics_link] görüntüleyebilir ve aşağıdan devre dışı bırakmayı seçebilirsiniz.",
|
||||
"opt_in": "Etkinleştir",
|
||||
"opt_out": "Devre dışı bırak",
|
||||
"cache_title": "Önbellek yönetimi",
|
||||
"cache_description": "Dönüştürücü dosyalarını tarayıcınızda önbelleğe alırız, böylece her seferinde yeniden indirmenize gerek kalmaz, performans artar ve veri kullanımı azalır.",
|
||||
"refresh_cache": "Önbelleği Yenile",
|
||||
"clear_cache": "Önbelleği Temizle",
|
||||
"files_cached": "{size} ({count} dosya)",
|
||||
"loading_cache": "Yükleniyor...",
|
||||
"total_size": "Toplam Boyut",
|
||||
"files_cached_label": "Önbelleğe Alınan Dosyalar",
|
||||
"cache_cleared": "Önbellek başarıyla temizlendi."
|
||||
},
|
||||
"language": {
|
||||
"title": "Dil",
|
||||
"description": "VERT arayüzü için tercih ettiğiniz dili seçin."
|
||||
}
|
||||
},
|
||||
"about": {
|
||||
"title": "Hakkımızda",
|
||||
"why": {
|
||||
"title": "Neden VERT?",
|
||||
"description": "<b>Dosya dönüştürücüler bizi her zaman hayal kırıklığına uğratmıştır. </b> Çoğu dönüştürücü site, kötü ve reklamlarla dolu arayüze sahiptir ve en önemlisi yavaştır. Tüm bu sorunları ve daha fazlasını çözen bir alternatif oluşturarak bu sorunu sonsuza kadar çözmeye karar verdik. <br/><br/>Video dışındaki tüm dosyalar tamamen cihazınızda dönüştürülür; bu, sunucuya dosya yükleme ve sunucudan dosya indirme sırasında gecikme olmaması ve dönüştürdüğünüz dosyaların asla başka biri tarafından görüntülenememesi anlamına gelir. <br/><br/>Video dosyaları, ışık hızındaki RTX 4000 Ada sunucumuza yüklenir. Videolarınızı dönüştürseniz de dönüştürmeseniz de bir saat sonra sunucularımızdan silinir. Video dönüştürme işlemi gerçekleştirirseniz, bir saat içinde dönüştürülmüş dosyayı indirebilirsiniz. Dosya daha sonra sunucumuzdan silinir."
|
||||
},
|
||||
"sponsors": {
|
||||
"title": "Sponsorlar",
|
||||
"description": "Bizi desteklemek ister misiniz? [discord_link]Discord[/discord_link] sunucumuzda bir geliştiriciyle iletişime geçin veya şu adrese e-posta gönderin:",
|
||||
"email_copied": "E-posta kopyalandı!"
|
||||
},
|
||||
"resources": {
|
||||
"title": "Bağlantılar",
|
||||
"discord": "Discord",
|
||||
"source": "GitHub",
|
||||
"email": "E-posta"
|
||||
},
|
||||
"donate": {
|
||||
"title": "VERT'e bağış yapın",
|
||||
"description": "Sizin desteğinizle VERT'i çalıştırmaya ve geliştirmeye devam edebiliriz.",
|
||||
"one_time": "Tek seferlik",
|
||||
"monthly": "Aylık",
|
||||
"custom": "Özel",
|
||||
"pay_now": "Ödeme yap",
|
||||
"donate_amount": "${amount} USD Bağış Yap",
|
||||
"thank_you": "Bağışınız için teşekkür ederiz!",
|
||||
"payment_failed": "Ödeme başarısız: {message}{period} Kartınızdan para çekilmedi.",
|
||||
"donation_error": "Bağışınız işlenirken bir hata oluştu. Lütfen daha sonra tekrar deneyin.",
|
||||
"payment_error": "Ödeme bilgileri alınırken hata oluştu. Lütfen daha sonra tekrar deneyin."
|
||||
},
|
||||
"credits": {
|
||||
"title": "Katkıda bulunanlar",
|
||||
"contact_team": "Geliştirme ekibiyle iletişime geçmek isterseniz, \"Bağlantılar\" kısmında bulunan e-posta adresini kullanabilirsiniz.",
|
||||
"notable_contributors": "Önemli katılımcılar",
|
||||
"notable_description": "VERT'e sağladıkları büyük katkılardan dolayı bu kişilere teşekkür ederiz.",
|
||||
"github_contributors": "GitHub katılımcıları",
|
||||
"github_description": "Yardımcı olan herkese çok [jpegify_link]teşekkürler[/jpegify_link]! [github_link]Sen de yardım etmek ister misin?[/github_link]",
|
||||
"no_contributors": "Henüz kimse katkıda bulunmamış gibi görünüyor... [contribute_link]ilk katkıda bulunan sen ol![/contribute_link]",
|
||||
"libraries": "Kütüphaneler",
|
||||
"libraries_description": "Bu mükemmel kütüphaneleri yıllardır geliştirdikleri için FFmpeg (ses, video), ImageMagick (görseller) ve Pandoc (belgeler)'a çok teşekkür ederiz. VERT, dönüştürme işlemlerini için bu kütüphaneleri kullanmaktadır.",
|
||||
"roles": {
|
||||
"lead_developer": "Lead developer; conversion backend, UI implementation",
|
||||
"developer": "Developer; UI implementation",
|
||||
"designer": "Designer; UX, branding, marketing",
|
||||
"docker_ci": "Maintaining Docker & CI support",
|
||||
"former_cofounder": "Former co-founder & designer"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"github_contributors": "GitHub katılımcılarını yüklerken hata oluştu"
|
||||
}
|
||||
},
|
||||
"workers": {
|
||||
"errors": {
|
||||
"general": "{dosya} dönüştürülürken hata oluştu: {message}",
|
||||
"cancel": "{dosya} için dönüştürme işlemi iptal edilirken hata oluştu: {message}",
|
||||
"magick": "Magick işlemi sırasında hata oluştu, görsel dönüştürme işlemi beklendiği gibi çalışmayabilir.",
|
||||
"ffmpeg": "ffmpeg yüklenirken hata oluştu, bazı özellikler çalışmayabilir.",
|
||||
"no_audio": "Ses akışı bulunamadı.",
|
||||
"invalid_rate": "Geçersiz örnekleme hızı: {hız}Hz"
|
||||
}
|
||||
},
|
||||
"jpegify": {
|
||||
"title": "GİZLİ JPEGIFY!!!",
|
||||
"subtitle": "(şşş... kimseye söyleme!)",
|
||||
"button": "JPEGIFY {compression}%!!!",
|
||||
"download": "İndir",
|
||||
"delete": "Sil"
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://inlang.com/schema/project-settings",
|
||||
"baseLocale": "en",
|
||||
"locales": ["en", "es", "fr", "de", "hr"],
|
||||
"locales": ["en", "es", "fr", "de", "hr", "tr", "ja"],
|
||||
"modules": [
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<script lang="ts" module>
|
||||
export interface VertdErrorProps {
|
||||
jobId: string;
|
||||
auth: string;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { vertdFetch } from "$lib/converters/vertd.svelte";
|
||||
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { ToastManager, type ToastProps } from "$lib/toast/index.svelte";
|
||||
|
||||
const toast: ToastProps<VertdErrorProps> = $props();
|
||||
|
||||
let submitting = $state(false);
|
||||
|
||||
export const title = "An error occurred";
|
||||
|
||||
const remove = () => {
|
||||
ToastManager.remove(toast.id);
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
submitting = true;
|
||||
try {
|
||||
await submitInner();
|
||||
} catch (e) {}
|
||||
submitting = false;
|
||||
};
|
||||
|
||||
const submitInner = async () => {
|
||||
try {
|
||||
await vertdFetch(
|
||||
"/api/keep",
|
||||
{
|
||||
method: "POST",
|
||||
},
|
||||
{
|
||||
token: toast.additional.auth,
|
||||
id: toast.additional.jobId,
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["convert.errors.vertd_failed_to_keep"]({
|
||||
error: (e as Error).message || e || "Unknown error",
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
ToastManager.remove(toast.id);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<p class="text-black">{m["convert.errors.vertd_generic_body"]()}</p>
|
||||
<div class="flex gap-4">
|
||||
<button
|
||||
onclick={submit}
|
||||
class="btn rounded-lg h-fit py-2 w-full bg-accent-red-alt text-white"
|
||||
disabled={submitting}
|
||||
>{m["convert.errors.vertd_generic_yes"]()}</button
|
||||
>
|
||||
<button
|
||||
onclick={remove}
|
||||
class="btn rounded-lg h-fit py-2 w-full"
|
||||
disabled={submitting}
|
||||
>{m["convert.errors.vertd_generic_no"]()}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
|
@ -1,19 +1,14 @@
|
|||
<script lang="ts">
|
||||
import Toast from "$lib/components/visual/Toast.svelte";
|
||||
import { type Toast as ToastType, toasts } from "$lib/store/ToastProvider";
|
||||
|
||||
let toastList = $state<ToastType[]>([]);
|
||||
toasts.subscribe((value) => {
|
||||
toastList = value as ToastType[];
|
||||
});
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="fixed bottom-28 md:bottom-0 right-0 p-4 flex flex-col-reverse gap-4 z-50"
|
||||
>
|
||||
{#each toastList as { id, type, message, durations }}
|
||||
{#each ToastManager.toasts as toast (toast.id)}
|
||||
<div class="flex justify-end">
|
||||
<Toast {id} {type} {message} {durations} />
|
||||
<Toast {toast} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -8,20 +8,20 @@
|
|||
XIcon,
|
||||
} from "lucide-svelte";
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { removeToast } from "$lib/store/ToastProvider";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
import type { ToastProps } from "$lib/toast/index.svelte";
|
||||
import type { SvelteComponent } from "svelte";
|
||||
import clsx from "clsx";
|
||||
import type { Toast as ToastType } from "$lib/toast/index.svelte";
|
||||
|
||||
type Props = {
|
||||
id: number;
|
||||
type: "success" | "error" | "info" | "warning";
|
||||
message: string;
|
||||
durations: {
|
||||
enter: number;
|
||||
stay: number;
|
||||
exit: number;
|
||||
};
|
||||
};
|
||||
const props: {
|
||||
toast: ToastType<unknown>;
|
||||
} = $props();
|
||||
|
||||
let { id, type, message, durations }: Props = $props();
|
||||
const { id, type, message, durations } = props.toast;
|
||||
|
||||
const additional =
|
||||
"additional" in props.toast ? props.toast.additional : {};
|
||||
|
||||
const colors = {
|
||||
success: "purple",
|
||||
|
@ -40,6 +40,9 @@
|
|||
let color = $derived(colors[type]);
|
||||
let Icon = $derived(Icons[type]);
|
||||
|
||||
let msg = $state<SvelteComponent<ToastProps>>();
|
||||
const title = $derived(((msg as any)?.title as string) ?? "");
|
||||
|
||||
// intentionally unused. this is so tailwind can generate the css for these colours as it doesn't detect if it's dynamically loaded
|
||||
// this would lead to the colours not being generated in the final css file by tailwind
|
||||
const colourVariants = [
|
||||
|
@ -51,7 +54,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-between max-w-[100%] md:max-w-md p-4 gap-4 bg-accent-{color} border-accent-{color}-alt border-l-4 rounded-lg shadow-md"
|
||||
class="flex flex-col max-w-[100%] md:max-w-md p-4 gap-2 bg-accent-{color} border-accent-{color}-alt border-l-4 rounded-lg shadow-md"
|
||||
in:fly={{
|
||||
duration: durations.enter,
|
||||
easing: quintOut,
|
||||
|
@ -63,21 +66,40 @@
|
|||
easing: quintOut,
|
||||
}}
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<Icon
|
||||
class="w-6 h-6 text-black flex-shrink-0"
|
||||
size="32"
|
||||
stroke="2"
|
||||
fill="none"
|
||||
/>
|
||||
<p class="text-black font-normal whitespace-pre-wrap break-all">
|
||||
{message}
|
||||
</p>
|
||||
<div class="flex flex-row items-center justify-between w-full gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon
|
||||
class="w-6 h-6 text-black flex-shrink-0"
|
||||
size="24"
|
||||
stroke="2"
|
||||
fill="none"
|
||||
/>
|
||||
<p
|
||||
class={clsx("text-black whitespace-pre-wrap", {
|
||||
"font-normal": !title,
|
||||
})}
|
||||
>
|
||||
{title || message}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="text-gray-600 hover:text-black flex-shrink-0"
|
||||
onclick={() => ToastManager.remove(id)}
|
||||
>
|
||||
<XIcon size="16" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="text-gray-600 hover:text-black flex-shrink-0"
|
||||
onclick={() => removeToast(id)}
|
||||
>
|
||||
<XIcon size="16" />
|
||||
</button>
|
||||
{#if typeof message !== "string"}
|
||||
{@const MessageComponent = message}
|
||||
<div class="font-normal">
|
||||
<MessageComponent
|
||||
bind:this={msg}
|
||||
{durations}
|
||||
{id}
|
||||
{message}
|
||||
{type}
|
||||
{additional}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -3,9 +3,9 @@ import { Converter, FormatInfo } from "./converter.svelte";
|
|||
import { FFmpeg } from "@ffmpeg/ffmpeg";
|
||||
import { browser } from "$app/environment";
|
||||
import { error, log } from "$lib/logger";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { Settings } from "$lib/sections/settings/index.svelte";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
|
||||
// TODO: differentiate in UI? (not native formats)
|
||||
const videoFormats = [
|
||||
|
@ -98,7 +98,10 @@ export class FFmpegConverter extends Converter {
|
|||
} catch (err) {
|
||||
error(["converters", this.name], `error loading ffmpeg: ${err}`);
|
||||
this.status = "error";
|
||||
addToast("error", m["workers.errors.ffmpeg"]());
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["workers.errors.ffmpeg"](),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { browser } from "$app/environment";
|
||||
import { error, log } from "$lib/logger";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { VertFile, type WorkerMessage } from "$lib/types";
|
||||
import MagickWorker from "$lib/workers/magick?worker&url";
|
||||
import { Converter, FormatInfo } from "./converter.svelte";
|
||||
import { imageFormats } from "./magick-automated";
|
||||
import { Settings } from "$lib/sections/settings/index.svelte";
|
||||
import magickWasm from "@imagemagick/magick-wasm/magick.wasm?url";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
|
||||
export class MagickConverter extends Converter {
|
||||
public name = "imagemagick";
|
||||
|
@ -104,7 +104,11 @@ export class MagickConverter extends Converter {
|
|||
["converters", this.name],
|
||||
`Failed to load ImageMagick WASM: ${err}`,
|
||||
);
|
||||
addToast("error", m["workers.errors.magick"]());
|
||||
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["workers.errors.magick"](),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,9 @@ import { VertFile, type WorkerMessage } from "$lib/types";
|
|||
import { Converter, FormatInfo } from "./converter.svelte";
|
||||
import { browser } from "$app/environment";
|
||||
import PandocWorker from "$lib/workers/pandoc?worker&url";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { error, log } from "$lib/logger";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
|
||||
export class PandocConverter extends Converter {
|
||||
public name = "pandoc";
|
||||
|
@ -25,7 +26,10 @@ export class PandocConverter extends Converter {
|
|||
this.status = "ready";
|
||||
} catch (err) {
|
||||
this.status = "error";
|
||||
addToast("error", `Failed to load Pandoc worker: ${err}`);
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: `Failed to load Pandoc worker: ${err}`, // TODO: i18n
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import VertdErrorComponent from "$lib/components/functional/VertdError.svelte";
|
||||
import { error, log } from "$lib/logger";
|
||||
import { Settings } from "$lib/sections/settings/index.svelte";
|
||||
import { VertdInstance } from "$lib/sections/settings/vertdSettings.svelte";
|
||||
|
@ -25,19 +26,45 @@ interface UploadResponse {
|
|||
totalFrames: number;
|
||||
}
|
||||
|
||||
interface RouteMap {
|
||||
"/api/upload": UploadResponse;
|
||||
"/api/version": string;
|
||||
interface RouteRequestMap {
|
||||
"/api/keep": {
|
||||
id: string;
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
|
||||
const vertdFetch = async <U extends keyof RouteMap>(
|
||||
url: U,
|
||||
options: RequestInit,
|
||||
): Promise<RouteMap[U]> => {
|
||||
interface RouteResponseMap {
|
||||
"/api/upload": UploadResponse;
|
||||
"/api/version": string;
|
||||
"/api/keep": void;
|
||||
}
|
||||
|
||||
export const vertdFetch: {
|
||||
<U extends keyof RouteRequestMap>(
|
||||
url: U,
|
||||
options: RequestInit,
|
||||
body: RouteRequestMap[U],
|
||||
): Promise<RouteResponseMap[U]>;
|
||||
<U extends Exclude<keyof RouteResponseMap, keyof RouteRequestMap>>(
|
||||
url: U,
|
||||
options: RequestInit,
|
||||
): Promise<RouteResponseMap[U]>;
|
||||
} = async (url: any, options: RequestInit, body?: any) => {
|
||||
const domain = await VertdInstance.instance.url();
|
||||
const res = await fetch(`${domain}${url}`, options);
|
||||
|
||||
// if there is a body, insert a Content-Type: application/json header
|
||||
if (body) {
|
||||
options.headers = {
|
||||
"Content-Type": "application/json",
|
||||
...(options.headers || {}),
|
||||
};
|
||||
options.body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
const res = await fetch(domain + url, options);
|
||||
|
||||
const text = await res.text();
|
||||
let json: VertdResponse<RouteMap[U]> = null!;
|
||||
let json = null;
|
||||
try {
|
||||
json = JSON.parse(text);
|
||||
} catch {
|
||||
|
@ -48,7 +75,7 @@ const vertdFetch = async <U extends keyof RouteMap>(
|
|||
throw new Error(json.data);
|
||||
}
|
||||
|
||||
return json.data as RouteMap[U];
|
||||
return json.data;
|
||||
};
|
||||
|
||||
// ws types
|
||||
|
@ -345,7 +372,13 @@ export class VertdConverter extends Converter {
|
|||
case "error": {
|
||||
this.log(`error: ${msg.data.message}`);
|
||||
this.activeConversions.delete(input.id);
|
||||
reject(msg.data.message);
|
||||
reject({
|
||||
component: VertdErrorComponent,
|
||||
additional: {
|
||||
jobId: uploadRes.id,
|
||||
auth: uploadRes.auth,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
import FancyInput from "$lib/components/functional/FancyInput.svelte";
|
||||
import Panel from "$lib/components/visual/Panel.svelte";
|
||||
import { effects } from "$lib/store/index.svelte";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import {
|
||||
loadStripe,
|
||||
type Stripe,
|
||||
|
@ -39,6 +38,7 @@
|
|||
import { Elements, PaymentElement } from "svelte-stripe";
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
|
||||
let amount = $state(1);
|
||||
let customAmount = $state("");
|
||||
|
@ -67,7 +67,10 @@
|
|||
|
||||
if (!res.ok) {
|
||||
paymentState = "prepay";
|
||||
addToast("error", m["about.donate.payment_error"]());
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["about.donate.payment_error"](),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -97,13 +100,13 @@
|
|||
const submitResult = await elements.submit();
|
||||
if (submitResult.error) {
|
||||
const period = submitResult.error.message?.endsWith(".") ? "" : ".";
|
||||
addToast(
|
||||
"error",
|
||||
m["about.donate.payment_failed"]({
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["about.donate.payment_failed"]({
|
||||
message: submitResult.error.message || "",
|
||||
period,
|
||||
}),
|
||||
);
|
||||
});
|
||||
enablePay = true;
|
||||
return;
|
||||
}
|
||||
|
@ -119,15 +122,18 @@
|
|||
|
||||
if (res.error) {
|
||||
const period = res.error.message?.endsWith(".") ? "" : ".";
|
||||
addToast(
|
||||
"error",
|
||||
m["about.donate.payment_failed"]({
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["about.donate.payment_failed"]({
|
||||
message: res.error.message || "",
|
||||
period,
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
addToast("success", m["about.donate.thank_you"]());
|
||||
ToastManager.add({
|
||||
type: "info",
|
||||
message: m["about.donate.thank_you"](),
|
||||
});
|
||||
}
|
||||
|
||||
paymentState = "prepay";
|
||||
|
@ -146,10 +152,16 @@
|
|||
if (status) {
|
||||
switch (status) {
|
||||
case "succeeded":
|
||||
addToast("success", m["about.donate.thank_you"]());
|
||||
ToastManager.add({
|
||||
type: "success",
|
||||
message: m["about.donate.thank_you"](),
|
||||
});
|
||||
break;
|
||||
default:
|
||||
addToast("error", m["about.donate.donation_error"]());
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["about.donate.donation_error"](),
|
||||
});
|
||||
}
|
||||
|
||||
goto("/about");
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
import HotMilk from "$lib/assets/hotmilk.svg?component";
|
||||
import { DISCORD_URL } from "$lib/consts";
|
||||
import { error } from "$lib/logger";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { link } from "$lib/store/index.svelte";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
|
||||
let copied = false;
|
||||
let timeoutId: number | undefined;
|
||||
|
@ -15,7 +15,10 @@
|
|||
try {
|
||||
navigator.clipboard.writeText("hello@vert.sh");
|
||||
copied = true;
|
||||
addToast("success", m["about.sponsors.email_copied"]());
|
||||
ToastManager.add({
|
||||
type: "success",
|
||||
message: m["about.sponsors.email_copied"](),
|
||||
});
|
||||
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => (copied = false), 2000);
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
import { m } from "$lib/paraglide/messages";
|
||||
import { link } from "$lib/store/index.svelte";
|
||||
import { swManager, type CacheInfo } from "$lib/sw/register";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { onMount } from "svelte";
|
||||
import { error } from "$lib/logger";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
|
||||
const { settings = $bindable() }: { settings: ISettings } = $props();
|
||||
|
||||
|
@ -50,10 +50,16 @@
|
|||
await swManager.clearCache();
|
||||
cacheInfo = null;
|
||||
await loadCacheInfo();
|
||||
addToast("success", m["settings.privacy.cache_cleared"]());
|
||||
ToastManager.add({
|
||||
type: "success",
|
||||
message: m["settings.privacy.cache_cleared"](),
|
||||
});
|
||||
} catch (err) {
|
||||
error(["privacy", "cache"], "Failed to clear cache:", err);
|
||||
addToast("error", "Failed to clear cache");
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["settings.privacy.cache_clear_error"](),
|
||||
});
|
||||
} finally {
|
||||
isLoadingCache = false;
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
import { writable } from "svelte/store";
|
||||
|
||||
export type ToastType = "success" | "error" | "info" | "warning";
|
||||
|
||||
export interface Toast {
|
||||
id: number;
|
||||
type: ToastType;
|
||||
message: string;
|
||||
disappearing: boolean;
|
||||
durations: {
|
||||
enter: number;
|
||||
stay: number;
|
||||
exit: number;
|
||||
};
|
||||
}
|
||||
|
||||
const toasts = writable<Toast[]>([]);
|
||||
|
||||
let toastId = 0;
|
||||
|
||||
function addToast(
|
||||
type: ToastType,
|
||||
message: string,
|
||||
disappearing?: boolean,
|
||||
durations?: { enter: number; stay: number; exit: number },
|
||||
) {
|
||||
const id = toastId++;
|
||||
|
||||
durations = durations ?? {
|
||||
enter: 300,
|
||||
stay: disappearing || disappearing === undefined ? 5000 : 86400000, // 24h cause why not
|
||||
exit: 500,
|
||||
};
|
||||
|
||||
const newToast: Toast = {
|
||||
id,
|
||||
type,
|
||||
message,
|
||||
disappearing: disappearing ?? true,
|
||||
durations,
|
||||
};
|
||||
toasts.update((currentToasts) => [...currentToasts, newToast]);
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
removeToast(id);
|
||||
},
|
||||
durations.enter + durations.stay + durations.exit,
|
||||
);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
function removeToast(id: number) {
|
||||
toasts.update((currentToasts) =>
|
||||
currentToasts.filter((toast) => toast.id !== id),
|
||||
);
|
||||
}
|
||||
|
||||
export { toasts, addToast, removeToast };
|
|
@ -323,6 +323,8 @@ export const availableLocales = {
|
|||
fr: "Français",
|
||||
de: "Deutsch",
|
||||
hr: "Hrvatski",
|
||||
tr: "Türkçe",
|
||||
ja: "日本語",
|
||||
};
|
||||
|
||||
export function updateLocale(newLocale: string) {
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
import type { Component } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export type ToastType = "success" | "error" | "info" | "warning";
|
||||
|
||||
// export interface Toast<
|
||||
// T = unknown,
|
||||
// U extends string | ToastComponent<T> = string | ToastComponent<T>,
|
||||
// > {
|
||||
// id: number;
|
||||
// type: ToastType;
|
||||
// message: U;
|
||||
// disappearing: boolean;
|
||||
// durations: {
|
||||
// enter: number;
|
||||
// stay: number;
|
||||
// exit: number;
|
||||
// };
|
||||
// additional: U extends string ? undefined : T;
|
||||
// }
|
||||
|
||||
type BaseToast = {
|
||||
id: number;
|
||||
type: ToastType;
|
||||
disappearing: boolean;
|
||||
durations: {
|
||||
enter: number;
|
||||
stay: number;
|
||||
exit: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type StringToast = BaseToast & {
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type ComponentToast<T> = BaseToast & {
|
||||
message: ToastComponent<T>;
|
||||
additional: T;
|
||||
};
|
||||
|
||||
export type Toast<T = unknown> = StringToast | ComponentToast<T>;
|
||||
|
||||
export type ToastProps<T = unknown> = Omit<ComponentToast<T>, "disappearing">;
|
||||
|
||||
export type ToastExports = {
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export type ToastComponent<T> = Component<ToastProps<T>, ToastExports>;
|
||||
|
||||
// export interface ToastOptions<T = unknown> {
|
||||
// type?: ToastType;
|
||||
// message: string | ToastComponent<T>;
|
||||
// disappearing?: boolean;
|
||||
// durations?: {
|
||||
// enter?: number;
|
||||
// stay?: number;
|
||||
// exit?: number;
|
||||
// };
|
||||
// additional?: T;
|
||||
// }
|
||||
|
||||
type RecursivePartial<T> = {
|
||||
[P in keyof T]?: T[P] extends (infer U)[]
|
||||
? RecursivePartial<U>[]
|
||||
: T[P] extends object | undefined
|
||||
? RecursivePartial<T[P]>
|
||||
: T[P];
|
||||
};
|
||||
|
||||
type BaseToastOptions = Omit<RecursivePartial<BaseToast>, "id"> & {
|
||||
disappearing?: boolean;
|
||||
};
|
||||
|
||||
export type StringToastOptions = BaseToastOptions & {
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type ComponentToastOptions<T> = BaseToastOptions & {
|
||||
message: ToastComponent<T>;
|
||||
additional: T;
|
||||
};
|
||||
|
||||
export type ToastOptions<T = unknown> =
|
||||
| StringToastOptions
|
||||
| ComponentToastOptions<T>;
|
||||
|
||||
// const toasts = writable<Toast[]>([]);
|
||||
|
||||
// let toastId = 0;
|
||||
|
||||
// function addToast(
|
||||
// type: ToastType,
|
||||
// message: string | Component,
|
||||
// disappearing?: boolean,
|
||||
// durations?: { enter: number; stay: number; exit: number },
|
||||
// ) {
|
||||
// const id = toastId++;
|
||||
|
||||
// durations = durations ?? {
|
||||
// enter: 300,
|
||||
// stay: disappearing || disappearing === undefined ? 5000 : 86400000, // 24h cause why not
|
||||
// exit: 500,
|
||||
// };
|
||||
|
||||
// const newToast: Toast = {
|
||||
// id,
|
||||
// type,
|
||||
// message,
|
||||
// disappearing: disappearing ?? true,
|
||||
// durations,
|
||||
// };
|
||||
// toasts.update((currentToasts) => [...currentToasts, newToast]);
|
||||
|
||||
// setTimeout(
|
||||
// () => {
|
||||
// removeToast(id);
|
||||
// },
|
||||
// durations.enter + durations.stay + durations.exit,
|
||||
// );
|
||||
|
||||
// return id;
|
||||
// }
|
||||
|
||||
// function removeToast(id: number) {
|
||||
// toasts.update((currentToasts) =>
|
||||
// currentToasts.filter((toast) => toast.id !== id),
|
||||
// );
|
||||
// }
|
||||
|
||||
// export { toasts, addToast, removeToast };
|
||||
|
||||
// const DURATION_DEFAULTS = {
|
||||
// enter: 300,
|
||||
// stay: 5000,
|
||||
// exit: 500,
|
||||
// };
|
||||
|
||||
const durationDefault = (disappearing: boolean) => ({
|
||||
enter: 300,
|
||||
stay: disappearing ? 5000 : 86400000, // 24h cause why not
|
||||
exit: 500,
|
||||
});
|
||||
|
||||
// const toastState = {
|
||||
// toasts: $state<Toast[]>([]),
|
||||
// };
|
||||
|
||||
class ToastState {
|
||||
private pId = $state(0);
|
||||
private pToasts = $state<Toast<unknown>[]>([]);
|
||||
|
||||
public add<T>(toast: Toast<T>) {
|
||||
this.pToasts.push(toast as Toast<unknown>);
|
||||
}
|
||||
|
||||
public remove(id: number) {
|
||||
this.pToasts = this.pToasts.filter((toast) => toast.id !== id);
|
||||
}
|
||||
|
||||
public id(): number {
|
||||
return this.pId++;
|
||||
}
|
||||
|
||||
public get toasts() {
|
||||
return this.pToasts;
|
||||
}
|
||||
}
|
||||
|
||||
export class ToastManager {
|
||||
static pToasts = new ToastState();
|
||||
|
||||
public static add<T = unknown>(toastOptions: ToastOptions<T>): number {
|
||||
const id = this.pToasts.id();
|
||||
const {
|
||||
type = "info",
|
||||
disappearing = true,
|
||||
durations: d = durationDefault(toastOptions.disappearing ?? true),
|
||||
} = toastOptions;
|
||||
const durations = {
|
||||
...durationDefault(disappearing),
|
||||
...d,
|
||||
};
|
||||
|
||||
if (typeof toastOptions.message === "string") {
|
||||
const newToast: StringToast = {
|
||||
id,
|
||||
type,
|
||||
message: toastOptions.message,
|
||||
disappearing,
|
||||
durations,
|
||||
};
|
||||
|
||||
this.pToasts.add(newToast);
|
||||
} else {
|
||||
const newToast: ComponentToast<T> = {
|
||||
id,
|
||||
type,
|
||||
message: toastOptions.message,
|
||||
disappearing,
|
||||
durations,
|
||||
additional: (toastOptions as ComponentToastOptions<T>)
|
||||
.additional,
|
||||
};
|
||||
|
||||
this.pToasts.add(newToast);
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
() => {
|
||||
this.remove(id);
|
||||
},
|
||||
durations.enter + durations.stay + durations.exit,
|
||||
);
|
||||
return id;
|
||||
}
|
||||
|
||||
public static remove(id: number) {
|
||||
this.pToasts.remove(id);
|
||||
}
|
||||
|
||||
public static get toasts() {
|
||||
return this.pToasts.toasts;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,8 @@ import { byNative, converters } from "$lib/converters";
|
|||
import type { Converter } from "$lib/converters/converter.svelte";
|
||||
import { error } from "$lib/logger";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
import type { Component } from "svelte";
|
||||
|
||||
export class VertFile {
|
||||
public id: string = Math.random().toString(36).slice(2, 8);
|
||||
|
@ -93,15 +94,7 @@ export class VertFile {
|
|||
this.result = res;
|
||||
} catch (err) {
|
||||
if (!this.cancelled) {
|
||||
const castedErr = err as Error;
|
||||
error(["files"], castedErr.message);
|
||||
addToast(
|
||||
"error",
|
||||
m["workers.errors.general"]({
|
||||
file: this.file.name,
|
||||
message: castedErr.message || castedErr,
|
||||
}),
|
||||
);
|
||||
this.toastErr(err);
|
||||
}
|
||||
this.result = null;
|
||||
}
|
||||
|
@ -119,15 +112,51 @@ export class VertFile {
|
|||
this.processing = false;
|
||||
this.result = null;
|
||||
} catch (err) {
|
||||
const castedErr = err as Error;
|
||||
error(["files"], castedErr.message);
|
||||
addToast(
|
||||
"error",
|
||||
m["workers.errors.cancel"]({
|
||||
this.toastErr(err);
|
||||
}
|
||||
}
|
||||
|
||||
private toastErr(err: unknown) {
|
||||
type ToastMsg = {
|
||||
component: Component;
|
||||
additional: unknown;
|
||||
};
|
||||
|
||||
const castedErr = err as Error | string | ToastMsg;
|
||||
let toastMsg: string | ToastMsg = "";
|
||||
if (typeof castedErr === "string") {
|
||||
toastMsg = castedErr;
|
||||
} else if (castedErr instanceof Error) {
|
||||
toastMsg = castedErr.message;
|
||||
} else {
|
||||
toastMsg = castedErr;
|
||||
}
|
||||
|
||||
// ToastManager.add({
|
||||
// type: "error",
|
||||
// message:
|
||||
// typeof toastMsg === "string"
|
||||
// ? m["workers.errors.general"]({
|
||||
// file: this.file.name,
|
||||
// message: toastMsg,
|
||||
// })
|
||||
// : toastMsg,
|
||||
// });
|
||||
|
||||
if (typeof toastMsg === "string") {
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["workers.errors.general"]({
|
||||
file: this.file.name,
|
||||
message: castedErr.message || castedErr,
|
||||
message: toastMsg,
|
||||
}),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: toastMsg.component,
|
||||
additional: toastMsg.additional,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@
|
|||
name="description"
|
||||
content="With VERT you can quickly convert any image, video and audio file. No ads, no tracking, open source, and all processing (other than video) is done on your device."
|
||||
/>
|
||||
<meta property="og:url" content="https://vert.sh">
|
||||
<meta property="og:type" content="website" />
|
||||
<meta
|
||||
property="og:title"
|
||||
|
@ -137,7 +138,9 @@
|
|||
content="With VERT you can quickly convert any image, video and audio file. No ads, no tracking, open source, and all processing (other than video) is done on your device."
|
||||
/>
|
||||
<meta property="og:image" content={featuredImage} />
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:domain" content="vert.sh">
|
||||
<meta property="twitter:url" content="https://vert.sh">
|
||||
<meta
|
||||
property="twitter:title"
|
||||
content="{VERT_NAME} — Free, fast, and awesome file converter"
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
import avatarRealmy from "$lib/assets/avatars/realmy.jpg";
|
||||
import avatarAzurejelly from "$lib/assets/avatars/azurejelly.jpg";
|
||||
import { GITHUB_API_URL } from "$lib/consts";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { dev } from "$app/environment";
|
||||
import { page } from "$app/state";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
// import { dev } from "$app/environment";
|
||||
// import { page } from "$app/state";
|
||||
|
||||
|
@ -81,7 +81,10 @@
|
|||
try {
|
||||
const response = await fetch(`${GITHUB_API_URL}/contributors`);
|
||||
if (!response.ok) {
|
||||
addToast("error", m["about.errors.github_contributors"]());
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["about.errors.github_contributors"](),
|
||||
});
|
||||
throw new Error(`HTTP error, status: ${response.status}`);
|
||||
}
|
||||
const allContribs = await response.json();
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
import { browser } from "$app/environment";
|
||||
import { log } from "$lib/logger";
|
||||
import * as Settings from "$lib/sections/settings/index.svelte";
|
||||
import { addToast } from "$lib/store/ToastProvider";
|
||||
import { PUB_PLAUSIBLE_URL } from "$env/static/public";
|
||||
import { SettingsIcon } from "lucide-svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { ToastManager } from "$lib/toast/index.svelte";
|
||||
|
||||
let settings = $state(Settings.Settings.instance.settings);
|
||||
|
||||
|
@ -32,7 +32,10 @@
|
|||
log(["settings"], "saving settings");
|
||||
} catch (error) {
|
||||
log(["settings", "error"], `failed to save settings: ${error}`);
|
||||
addToast("error", m["settings.errors.save_failed"]());
|
||||
ToastManager.add({
|
||||
type: "error",
|
||||
message: m["settings.errors.save_failed"](),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue