Files
exbyte-depot-viewer-perforce/src/i18n.ts
Bonchellon b00b4f94b0 Add Build Solution (MSBuild) with live output
find_sln locates a .sln in the working folder; build_sln locates MSBuild
via vswhere and builds the solution's default config (compiles the UE game
module for Unreal projects), streaming output as build-log events. A build
overlay shows the live log with error/warning coloring and a status. Wired
into Tools (Ctrl+B) and the Working-folder right-click menu when a .sln is
found. New strings translated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:04:03 +03:00

267 lines
35 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Lightweight i18n. English strings are the keys; translations live per-language.
// t() reads the module-level LANG, so any component re-rendered after a language
// change (the whole tree re-renders from App) picks up the new language.
export type Lang = "en" | "ru" | "de" | "fr" | "es";
export const LANGS: { code: Lang; name: string; flag: string }[] = [
{ code: "en", name: "English", flag: "🇬🇧" },
{ code: "ru", name: "Русский", flag: "🇷🇺" },
{ code: "de", name: "Deutsch", flag: "🇩🇪" },
{ code: "fr", name: "Français", flag: "🇫🇷" },
{ code: "es", name: "Español", flag: "🇪🇸" },
];
function load(): Lang {
try { const l = localStorage.getItem("exd-lang") as Lang; return LANGS.some((x) => x.code === l) ? l : "en"; }
catch { return "en"; }
}
export let LANG: Lang = load();
export function setLangGlobal(l: Lang) { LANG = l; try { localStorage.setItem("exd-lang", l); } catch {} }
type Tr = Partial<Record<Lang, string>>;
const D: Record<string, Tr> = {
// window controls
"Minimize": { ru: "Свернуть", de: "Minimieren", fr: "Réduire", es: "Minimizar" },
"Maximize": { ru: "Развернуть", de: "Maximieren", fr: "Agrandir", es: "Maximizar" },
"Close": { ru: "Закрыть", de: "Schließen", fr: "Fermer", es: "Cerrar" },
"Theme": { ru: "Тема", de: "Thema", fr: "Thème", es: "Tema" },
"Restoring session…": { ru: "Восстановление сессии…", de: "Sitzung wird wiederhergestellt…", fr: "Restauration de la session…", es: "Restaurando sesión…" },
// startup splash
"Connecting to server…": { ru: "Подключение к серверу…", de: "Verbinde mit Server…", fr: "Connexion au serveur…", es: "Conectando al servidor…" },
"Authenticating…": { ru: "Авторизация…", de: "Authentifizierung…", fr: "Authentification…", es: "Autenticando…" },
"Loading workspace…": { ru: "Загрузка workspace…", de: "Workspace wird geladen…", fr: "Chargement du workspace…", es: "Cargando workspace…" },
"Syncing state…": { ru: "Синхронизация…", de: "Zustand wird synchronisiert…", fr: "Synchronisation…", es: "Sincronizando…" },
// server transfer dialog
"Submitting to server": { ru: "Отправка на сервер", de: "Wird an Server gesendet", fr: "Envoi au serveur", es: "Enviando al servidor" },
"Getting latest from server": { ru: "Загрузка с сервера", de: "Neueste Version wird geladen", fr: "Récupération depuis le serveur", es: "Obteniendo del servidor" },
"{n} files": { ru: "{n} файлов", de: "{n} Dateien", fr: "{n} fichiers", es: "{n} archivos" },
"Uploading…": { ru: "Отправка…", de: "Wird hochgeladen…", fr: "Envoi…", es: "Subiendo…" },
"Downloading…": { ru: "Скачивание…", de: "Wird heruntergeladen…", fr: "Téléchargement…", es: "Descargando…" },
// connect screen
"Connect to Perforce": { ru: "Подключение к Perforce", de: "Mit Perforce verbinden", fr: "Se connecter à Perforce", es: "Conectar a Perforce" },
"Exbyte Depot — sign in to your P4 server": { ru: "Exbyte Depot — вход на ваш сервер P4", de: "Exbyte Depot — bei deinem P4-Server anmelden", fr: "Exbyte Depot — connectez-vous à votre serveur P4", es: "Exbyte Depot — inicia sesión en tu servidor P4" },
"Server": { ru: "Сервер", de: "Server", fr: "Serveur", es: "Servidor" },
"User": { ru: "Пользователь", de: "Benutzer", fr: "Utilisateur", es: "Usuario" },
"Password": { ru: "Пароль", de: "Passwort", fr: "Mot de passe", es: "Contraseña" },
"Workspace": { ru: "Workspace", de: "Workspace", fr: "Workspace", es: "Workspace" },
"workspace name (not a folder path!)": { ru: "имя workspace (не путь к папке!)", de: "Workspace-Name (kein Ordnerpfad!)", fr: "nom du workspace (pas un chemin !)", es: "nombre del workspace (¡no una ruta!)" },
"Connecting…": { ru: "Подключение…", de: "Verbinde…", fr: "Connexion…", es: "Conectando…" },
"Connect": { ru: "Подключиться", de: "Verbinden", fr: "Se connecter", es: "Conectar" },
// toolbar
"Switch workspace · right-click for more": { ru: "Сменить workspace · ПКМ — ещё", de: "Workspace wechseln · Rechtsklick für mehr", fr: "Changer de workspace · clic droit pour plus", es: "Cambiar workspace · clic derecho para más" },
"Choose the project working folder · right-click for more": { ru: "Выбрать рабочую папку проекта · ПКМ — ещё", de: "Projekt-Arbeitsordner wählen · Rechtsklick für mehr", fr: "Choisir le dossier de travail · clic droit pour plus", es: "Elegir carpeta de trabajo · clic derecho para más" },
"Open in Explorer": { ru: "Открыть в проводнике", de: "Im Explorer öffnen", fr: "Ouvrir dans lexplorateur", es: "Abrir en el explorador" },
"Switch workspace…": { ru: "Сменить workspace…", de: "Workspace wechseln…", fr: "Changer de workspace…", es: "Cambiar workspace…" },
"Copy name": { ru: "Скопировать имя", de: "Namen kopieren", fr: "Copier le nom", es: "Copiar nombre" },
"Choose another folder…": { ru: "Выбрать другую папку…", de: "Anderen Ordner wählen…", fr: "Choisir un autre dossier…", es: "Elegir otra carpeta…" },
"Copy path": { ru: "Скопировать путь", de: "Pfad kopieren", fr: "Copier le chemin", es: "Copiar ruta" },
"Working folder": { ru: "Рабочая папка", de: "Arbeitsordner", fr: "Dossier de travail", es: "Carpeta de trabajo" },
"whole workspace (//…)": { ru: "весь workspace (//…)", de: "gesamter Workspace (//…)", fr: "workspace entier (//…)", es: "todo el workspace (//…)" },
"Drag to resize": { ru: "Тянуть — изменить ширину", de: "Ziehen zum Anpassen", fr: "Glisser pour redimensionner", es: "Arrastra para redimensionar" },
"Submit committed to the server (push)": { ru: "Отправить закоммиченное на сервер (push)", de: "Committed an Server senden (push)", fr: "Envoyer les commits au serveur (push)", es: "Enviar lo confirmado al servidor (push)" },
"Submit to server": { ru: "Submit на сервер", de: "An Server senden", fr: "Envoyer au serveur", es: "Enviar al servidor" },
"{n} changelist(s) → push": { ru: "{n} changelist(ов) → push", de: "{n} Changelist(s) → push", fr: "{n} changelist(s) → push", es: "{n} changelist(s) → push" },
"Get Latest": { ru: "Get Latest", de: "Get Latest", fr: "Get Latest", es: "Get Latest" },
"Working…": { ru: "Работаю…", de: "Arbeite…", fr: "En cours…", es: "Trabajando…" },
"pull from server": { ru: "забрать с сервера (pull)", de: "vom Server holen (pull)", fr: "récupérer du serveur (pull)", es: "traer del servidor (pull)" },
"Launch": { ru: "Запустить", de: "Starten", fr: "Lancer", es: "Iniciar" },
"Launch Unreal Engine": { ru: "Запустить Unreal Engine", de: "Unreal Engine starten", fr: "Lancer Unreal Engine", es: "Iniciar Unreal Engine" },
"Build Solution (.sln)": { ru: "Собрать решение (.sln)", de: "Projektmappe bauen (.sln)", fr: "Compiler la solution (.sln)", es: "Compilar solución (.sln)" },
"Build Solution — no .sln": { ru: "Собрать решение — нет .sln", de: "Projektmappe bauen — keine .sln", fr: "Compiler — aucune .sln", es: "Compilar — sin .sln" },
"No .sln found in the working folder. Choose the project folder first.": { ru: "В рабочей папке нет .sln. Сначала выбери папку проекта.", de: "Keine .sln im Arbeitsordner. Wähle zuerst den Projektordner.", fr: "Aucune .sln dans le dossier de travail. Choisissez dabord le dossier du projet.", es: "No hay .sln en la carpeta de trabajo. Elige primero la carpeta del proyecto." },
"Build": { ru: "Сборка", de: "Build", fr: "Compilation", es: "Compilación" },
"Building…": { ru: "Сборка…", de: "Wird gebaut…", fr: "Compilation…", es: "Compilando…" },
"Build succeeded": { ru: "Сборка успешна", de: "Build erfolgreich", fr: "Compilation réussie", es: "Compilación correcta" },
"Build failed": { ru: "Сборка провалена", de: "Build fehlgeschlagen", fr: "Échec de la compilation", es: "Compilación fallida" },
"Starting MSBuild…": { ru: "Запуск MSBuild…", de: "MSBuild wird gestartet…", fr: "Démarrage de MSBuild…", es: "Iniciando MSBuild…" },
"Open this project in Unreal Engine": { ru: "Открыть проект в Unreal Engine", de: "Projekt in Unreal Engine öffnen", fr: "Ouvrir le projet dans Unreal Engine", es: "Abrir el proyecto en Unreal Engine" },
"Launching Unreal…": { ru: "Запуск Unreal…", de: "Unreal wird gestartet…", fr: "Lancement dUnreal…", es: "Iniciando Unreal…" },
// tabs / list
"Changes": { ru: "Changes", de: "Änderungen", fr: "Modifications", es: "Cambios" },
"History": { ru: "History", de: "Verlauf", fr: "Historique", es: "Historial" },
"Filter {n} files…": { ru: "Фильтр {n} файлов…", de: "{n} Dateien filtern…", fr: "Filtrer {n} fichiers…", es: "Filtrar {n} archivos…" },
"{n} changelist(s) committed — ready to Submit": { ru: "{n} changelist(ов) закоммичено — готово к Submit", de: "{n} Changelist(s) committed — bereit zum Submit", fr: "{n} changelist(s) validés — prêts à envoyer", es: "{n} changelist(s) confirmados — listos para enviar" },
"Scanning disk for changes…": { ru: "Ищу изменения на диске…", de: "Suche Änderungen auf der Festplatte…", fr: "Recherche de modifications sur le disque…", es: "Buscando cambios en el disco…" },
" for the whole workspace this is slow — pick a project folder above": { ru: " для всего workspace это долго — выбери рабочую папку проекта сверху", de: " für den ganzen Workspace ist das langsam — wähle oben einen Projektordner", fr: " pour tout le workspace cest lent — choisissez un dossier de projet en haut", es: " para todo el workspace es lento — elige una carpeta de proyecto arriba" },
"{a} of {b} selected": { ru: "{a} из {b} выбрано", de: "{a} von {b} ausgewählt", fr: "{a} sur {b} sélectionnés", es: "{a} de {b} seleccionados" },
"Select all": { ru: "Выделить все", de: "Alle auswählen", fr: "Tout sélectionner", es: "Seleccionar todo" },
"Deselect all": { ru: "Снять выделение", de: "Auswahl aufheben", fr: "Tout désélectionner", es: "Deseleccionar todo" },
"Refresh": { ru: "Обновить", de: "Aktualisieren", fr: "Rafraîchir", es: "Actualizar" },
"Scanning…": { ru: "Сканирую…", de: "Scanne…", fr: "Analyse…", es: "Analizando…" },
"Re-read open files (p4 opened)": { ru: "Перечитать открытые файлы (p4 opened)", de: "Offene Dateien neu lesen (p4 opened)", fr: "Relire les fichiers ouverts (p4 opened)", es: "Releer archivos abiertos (p4 opened)" },
"Loading…": { ru: "Загрузка…", de: "Lädt…", fr: "Chargement…", es: "Cargando…" },
"No open files yet.\nEdit assets in Unreal (Source Control → Perforce) — they show up here on their own.\n\nEdited files outside Perforce? Actions → “Rescan changes”.": {
ru: "Пока нет открытых файлов.\nМеняй ассеты в Unreal (Source Control → Perforce) — они появятся тут сами.\n\nПравил файлы мимо Perforce? Actions → «Найти изменения заново».",
de: "Noch keine offenen Dateien.\nBearbeite Assets in Unreal (Source Control → Perforce) — sie erscheinen hier von selbst.\n\nDateien außerhalb von Perforce geändert? Aktionen → „Änderungen neu suchen“.",
fr: "Aucun fichier ouvert pour linstant.\nModifiez les assets dans Unreal (Source Control → Perforce) — ils apparaîtront ici automatiquement.\n\nFichiers modifiés hors Perforce ? Actions → « Rechercher les modifications ».",
es: "Aún no hay archivos abiertos.\nEdita assets en Unreal (Source Control → Perforce) — aparecerán aquí solos.\n\n¿Editaste archivos fuera de Perforce? Acciones → «Volver a buscar cambios».",
},
// commit box
"Summary: what changed…": { ru: "Кратко: что за изменение…", de: "Kurz: was wurde geändert…", fr: "Résumé : quel changement…", es: "Resumen: qué cambió…" },
"Description (optional)…": { ru: "Описание (необязательно)…", de: "Beschreibung (optional)…", fr: "Description (facultatif)…", es: "Descripción (opcional)…" },
"Commit changelist": { ru: "Commit changelist", de: "Changelist committen", fr: "Valider le changelist", es: "Confirmar changelist" },
"· {n} files": { ru: "· {n} файлов", de: "· {n} Dateien", fr: "· {n} fichiers", es: "· {n} archivos" },
"Commit = local (revertable). Send to the server — Submit, top-right.": { ru: "Commit = локально (revertable). Отправка на сервер — Submit вверху справа.", de: "Commit = lokal (rückgängig machbar). Zum Server senden — Submit oben rechts.", fr: "Commit = local (réversible). Envoyer au serveur — Submit en haut à droite.", es: "Commit = local (reversible). Enviar al servidor — Submit arriba a la derecha." },
// menus
"File": { ru: "Файл", de: "Datei", fr: "Fichier", es: "Archivo" },
"Connection": { ru: "Подключение", de: "Verbindung", fr: "Connexion", es: "Conexión" },
"Actions": { ru: "Действия", de: "Aktionen", fr: "Actions", es: "Acciones" },
"Tools": { ru: "Инструменты", de: "Werkzeuge", fr: "Outils", es: "Herramientas" },
"Help": { ru: "Справка", de: "Hilfe", fr: "Aide", es: "Ayuda" },
"Choose working folder…": { ru: "Выбрать рабочую папку…", de: "Arbeitsordner wählen…", fr: "Choisir le dossier de travail…", es: "Elegir carpeta de trabajo…" },
"Disconnect": { ru: "Отключиться", de: "Trennen", fr: "Se déconnecter", es: "Desconectar" },
"Rescan changes": { ru: "Найти изменения заново", de: "Änderungen neu suchen", fr: "Rechercher les modifications", es: "Volver a buscar cambios" },
"Commit (local)": { ru: "Commit (локально)", de: "Commit (lokal)", fr: "Commit (local)", es: "Commit (local)" },
"Revert All…": { ru: "Откатить всё…", de: "Alles zurücksetzen…", fr: "Tout annuler…", es: "Revertir todo…" },
"Settings…": { ru: "Настройки…", de: "Einstellungen…", fr: "Paramètres…", es: "Ajustes…" },
"About": { ru: "О программе", de: "Über", fr: "À propos", es: "Acerca de" },
// confirm dialogs
"Revert all changes?": { ru: "Откатить все изменения?", de: "Alle Änderungen zurücksetzen?", fr: "Annuler toutes les modifications ?", es: "¿Revertir todos los cambios?" },
"Edits in all {n} files will be undone. Local changes are lost permanently.": { ru: "Будут отменены правки во всех {n} файлах. Локальные изменения потеряются безвозвратно.", de: "Änderungen in allen {n} Dateien werden verworfen. Lokale Änderungen gehen endgültig verloren.", fr: "Les modifications de {n} fichiers seront annulées. Les changements locaux sont perdus définitivement.", es: "Se desharán los cambios en {n} archivos. Los cambios locales se pierden para siempre." },
"Revert all": { ru: "Откатить всё", de: "Alles zurücksetzen", fr: "Tout annuler", es: "Revertir todo" },
"Submit — send to the server": { ru: "Submit — отправить на сервер", de: "Submit — an Server senden", fr: "Submit — envoyer au serveur", es: "Submit — enviar al servidor" },
"{n} changelist(s) will go to the Perforce depot and become available to the team. This is a push — it cannot be undone.": { ru: "{n} changelist(ов) уйдут в депо Perforce и станут доступны команде. Это push — отменить нельзя.", de: "{n} Changelist(s) gehen ins Perforce-Depot und werden für das Team verfügbar. Das ist ein Push — nicht widerrufbar.", fr: "{n} changelist(s) iront dans le dépôt Perforce et seront disponibles pour léquipe. Cest un push — irréversible.", es: "{n} changelist(s) irán al depósito Perforce y estarán disponibles para el equipo. Es un push — no se puede deshacer." },
"Submit {n}": { ru: "Отправить {n}", de: "{n} senden", fr: "Envoyer {n}", es: "Enviar {n}" },
"Changelist {a} of {b}": { ru: "Changelist {a} из {b}", de: "Changelist {a} von {b}", fr: "Changelist {a} sur {b}", es: "Changelist {a} de {b}" },
"Large assets take longer to upload — dont close the window.": { ru: "Крупные ассеты заливаются дольше — не закрывай окно.", de: "Große Assets brauchen länger zum Hochladen — schließe das Fenster nicht.", fr: "Les gros assets prennent plus de temps à envoyer — ne fermez pas la fenêtre.", es: "Los assets grandes tardan más en subir — no cierres la ventana." },
"Revert file?": { ru: "Откатить файл?", de: "Datei zurücksetzen?", fr: "Annuler le fichier ?", es: "¿Revertir archivo?" },
"{name}\n\nLocal edits to this file will be lost.": { ru: "{name}\n\nЛокальные правки этого файла будут потеряны.", de: "{name}\n\nLokale Änderungen an dieser Datei gehen verloren.", fr: "{name}\n\nLes modifications locales de ce fichier seront perdues.", es: "{name}\n\nLos cambios locales de este archivo se perderán." },
"Revert": { ru: "Откатить", de: "Zurücksetzen", fr: "Annuler", es: "Revertir" },
"Discard changes": { ru: "Отменить изменения", de: "Änderungen verwerfen", fr: "Abandonner les modifications", es: "Descartar cambios" },
"Discard changes · {n} files": { ru: "Отменить изменения · {n} файлов", de: "Änderungen verwerfen · {n} Dateien", fr: "Abandonner · {n} fichiers", es: "Descartar · {n} archivos" },
"Discard changes?": { ru: "Отменить изменения?", de: "Änderungen verwerfen?", fr: "Abandonner les modifications ?", es: "¿Descartar cambios?" },
"Discard": { ru: "Отменить", de: "Verwerfen", fr: "Abandonner", es: "Descartar" },
"Local changes to {name} will be lost permanently — the file returns to the server version.": { ru: "Локальные изменения в {name} будут потеряны безвозвратно — файл вернётся к версии с сервера.", de: "Lokale Änderungen an {name} gehen endgültig verloren — die Datei kehrt zur Serverversion zurück.", fr: "Les modifications locales de {name} seront perdues définitivement — le fichier revient à la version du serveur.", es: "Los cambios locales en {name} se perderán para siempre — el archivo vuelve a la versión del servidor." },
"Discarded: {name}": { ru: "Отменено: {name}", de: "Verworfen: {name}", fr: "Abandonné : {name}", es: "Descartado: {name}" },
"Edit in VS Code": { ru: "Открыть в VS Code", de: "In VS Code öffnen", fr: "Ouvrir dans VS Code", es: "Abrir en VS Code" },
"Opening in VS Code…": { ru: "Открываю в VS Code…", de: "Wird in VS Code geöffnet…", fr: "Ouverture dans VS Code…", es: "Abriendo en VS Code…" },
"Revert changelist #{n}?": { ru: "Откатить changelist #{n}?", de: "Changelist #{n} zurücksetzen?", fr: "Annuler le changelist #{n} ?", es: "¿Revertir changelist #{n}?" },
"Perforce will create a NEW changelist that undoes #{n} and submit it to the server. History is kept (like “Revert” in GitHub Desktop).\n\n“{desc}”": { ru: "Perforce создаст НОВЫЙ changelist, отменяющий изменения из #{n}, и засабмитит его на сервер. История сохранится (как «Revert» в GitHub Desktop).\n\n«{desc}»", de: "Perforce erstellt eine NEUE Changelist, die #{n} rückgängig macht, und sendet sie an den Server. Der Verlauf bleibt erhalten (wie „Revert“ in GitHub Desktop).\n\n„{desc}“", fr: "Perforce créera un NOUVEAU changelist qui annule #{n} et lenverra au serveur. Lhistorique est conservé (comme « Revert » dans GitHub Desktop).\n\n« {desc} »", es: "Perforce creará un NUEVO changelist que deshace #{n} y lo enviará al servidor. El historial se conserva (como «Revert» en GitHub Desktop).\n\n«{desc}»" },
"Revert #{n}": { ru: "Откатить #{n}", de: "#{n} zurücksetzen", fr: "Annuler #{n}", es: "Revertir #{n}" },
"no description": { ru: "без описания", de: "keine Beschreibung", fr: "sans description", es: "sin descripción" },
"Cancel": { ru: "Отмена", de: "Abbrechen", fr: "Annuler", es: "Cancelar" },
// toasts
"No session data for the workspace list": { ru: "Нет данных сессии для списка workspace", de: "Keine Sitzungsdaten für die Workspace-Liste", fr: "Aucune donnée de session pour la liste des workspaces", es: "No hay datos de sesión para la lista de workspaces" },
"Active workspace: {c}": { ru: "Активный workspace: {c}", de: "Aktiver Workspace: {c}", fr: "Workspace actif : {c}", es: "Workspace activo: {c}" },
"Get Latest: {msg}": { ru: "Get Latest: {msg}", de: "Get Latest: {msg}", fr: "Get Latest : {msg}", es: "Get Latest: {msg}" },
"Added/updated in changelist: {n}": { ru: "В changelist добавлено/обновлено: {n}", de: "Zur Changelist hinzugefügt/aktualisiert: {n}", fr: "Ajouté/mis à jour dans le changelist : {n}", es: "Añadido/actualizado en el changelist: {n}" },
"No changes to add": { ru: "Нет изменений для добавления", de: "Keine Änderungen zum Hinzufügen", fr: "Aucune modification à ajouter", es: "No hay cambios para añadir" },
"All changes reverted.": { ru: "Все изменения откачены.", de: "Alle Änderungen zurückgesetzt.", fr: "Toutes les modifications annulées.", es: "Todos los cambios revertidos." },
"Committed locally · changelist {cl}. Press Submit at the top to send it to the server.": { ru: "Закоммичено локально · changelist {cl}. Нажми Submit вверху, чтобы отправить на сервер.", de: "Lokal committed · Changelist {cl}. Drücke oben Submit, um an den Server zu senden.", fr: "Validé localement · changelist {cl}. Appuyez sur Submit en haut pour lenvoyer au serveur.", es: "Confirmado localmente · changelist {cl}. Pulsa Submit arriba para enviarlo al servidor." },
"Sent to the server: {n} changelist(s).": { ru: "Отправлено на сервер: {n} changelist(ов).", de: "An Server gesendet: {n} Changelist(s).", fr: "Envoyé au serveur : {n} changelist(s).", es: "Enviado al servidor: {n} changelist(s)." },
"Reverted: {name}": { ru: "Откачено: {name}", de: "Zurückgesetzt: {name}", fr: "Annulé : {name}", es: "Revertido: {name}" },
"Changelist #{n} reverted by a new changelist.": { ru: "Changelist #{n} откачен новым changelist'ом.", de: "Changelist #{n} durch eine neue Changelist zurückgesetzt.", fr: "Changelist #{n} annulé par un nouveau changelist.", es: "Changelist #{n} revertido por un nuevo changelist." },
"Path not set": { ru: "Путь не определён", de: "Pfad nicht festgelegt", fr: "Chemin non défini", es: "Ruta no definida" },
"{what} copied": { ru: "{what} скопировано", de: "{what} kopiert", fr: "{what} copié", es: "{what} copiado" },
"Copy failed": { ru: "Не удалось скопировать", de: "Kopieren fehlgeschlagen", fr: "Échec de la copie", es: "Error al copiar" },
"Workspace name": { ru: "Имя workspace", de: "Workspace-Name", fr: "Nom du workspace", es: "Nombre del workspace" },
"Path": { ru: "Путь", de: "Pfad", fr: "Chemin", es: "Ruta" },
"Number": { ru: "Номер", de: "Nummer", fr: "Numéro", es: "Número" },
"Description": { ru: "Описание", de: "Beschreibung", fr: "Description", es: "Descripción" },
"Found and added to Changes: {n}": { ru: "Найдено и добавлено в Changes: {n}", de: "Gefunden und zu Änderungen hinzugefügt: {n}", fr: "Trouvé et ajouté aux modifications : {n}", es: "Encontrado y añadido a Cambios: {n}" },
"No new or changed files found.": { ru: "Новых или изменённых файлов не найдено.", de: "Keine neuen oder geänderten Dateien gefunden.", fr: "Aucun fichier nouveau ou modifié trouvé.", es: "No se encontraron archivos nuevos o modificados." },
// drop zone
"Drop to add to Perforce": { ru: "Отпусти, чтобы добавить в Perforce", de: "Loslassen, um zu Perforce hinzuzufügen", fr: "Relâchez pour ajouter à Perforce", es: "Suelta para añadir a Perforce" },
// app modal — workspaces
"Choose workspace": { ru: "Выбор workspace", de: "Workspace wählen", fr: "Choisir un workspace", es: "Elegir workspace" },
"No workspaces available": { ru: "Нет доступных workspace", de: "Keine Workspaces verfügbar", fr: "Aucun workspace disponible", es: "No hay workspaces disponibles" },
"Active: {c}": { ru: "Активный: {c}", de: "Aktiv: {c}", fr: "Actif : {c}", es: "Activo: {c}" },
// app modal — scope
".. up": { ru: ".. вверх", de: ".. hoch", fr: ".. haut", es: ".. arriba" },
"No subfolders": { ru: "Вложенных папок нет", de: "Keine Unterordner", fr: "Aucun sous-dossier", es: "Sin subcarpetas" },
"Work in: {path}": { ru: "Работать в: {path}", de: "Arbeiten in: {path}", fr: "Travailler dans : {path}", es: "Trabajar en: {path}" },
"Work in the whole workspace (//…)": { ru: "Работать во всём workspace (//…)", de: "Im ganzen Workspace arbeiten (//…)", fr: "Travailler dans tout le workspace (//…)", es: "Trabajar en todo el workspace (//…)" },
// app modal — settings
"Settings": { ru: "Настройки", de: "Einstellungen", fr: "Paramètres", es: "Ajustes" },
"Language": { ru: "Язык", de: "Sprache", fr: "Langue", es: "Idioma" },
"Interface scale": { ru: "Масштаб интерфейса", de: "Oberflächenskalierung", fr: "Échelle de linterface", es: "Escala de interfaz" },
"Reset": { ru: "Сбросить", de: "Zurücksetzen", fr: "Réinitialiser", es: "Restablecer" },
// window menu + log panel
"Window": { ru: "Окно", de: "Fenster", fr: "Fenêtre", es: "Ventana" },
"Log": { ru: "Лог", de: "Protokoll", fr: "Journal", es: "Registro" },
"Clear": { ru: "Очистить", de: "Leeren", fr: "Effacer", es: "Limpiar" },
"item(s)": { ru: "элем.", de: "Einträge", fr: "élément(s)", es: "elemento(s)" },
"No commands yet.": { ru: "Пока нет команд.", de: "Noch keine Befehle.", fr: "Aucune commande pour linstant.", es: "Aún no hay comandos." },
"Recent commands": { ru: "Последние команды", de: "Letzte Befehle", fr: "Commandes récentes", es: "Comandos recientes" },
"Light ☀": { ru: "Светлая ☀", de: "Hell ☀", fr: "Clair ☀", es: "Claro ☀" },
"Dark ☾": { ru: "Тёмная ☾", de: "Dunkel ☾", fr: "Sombre ☾", es: "Oscuro ☾" },
"Server version": { ru: "Версия сервера", de: "Serverversion", fr: "Version du serveur", es: "Versión del servidor" },
// about
"Exbyte Depot — native Perforce client": { ru: "Exbyte Depot — нативный клиент Perforce", de: "Exbyte Depot — nativer Perforce-Client", fr: "Exbyte Depot — client Perforce natif", es: "Exbyte Depot — cliente nativo de Perforce" },
"Built by Exbyte Studios": { ru: "Собственная разработка Exbyte Studios", de: "Entwickelt von Exbyte Studios", fr: "Développé par Exbyte Studios", es: "Desarrollado por Exbyte Studios" },
// change detail
"Select a changelist in History": { ru: "Выберите changelist в History", de: "Wähle eine Changelist im Verlauf", fr: "Sélectionnez un changelist dans lhistorique", es: "Selecciona un changelist en el historial" },
"(no description)": { ru: "(без описания)", de: "(keine Beschreibung)", fr: "(sans description)", es: "(sin descripción)" },
"{user} · {time} · {n} file(s)": { ru: "{user} · {time} · {n} файл(ов)", de: "{user} · {time} · {n} Datei(en)", fr: "{user} · {time} · {n} fichier(s)", es: "{user} · {time} · {n} archivo(s)" },
"Loading file list…": { ru: "Загрузка списка файлов…", de: "Dateiliste wird geladen…", fr: "Chargement de la liste des fichiers…", es: "Cargando lista de archivos…" },
// history list
"Loading history…": { ru: "Загрузка истории…", de: "Verlauf wird geladen…", fr: "Chargement de lhistorique…", es: "Cargando historial…" },
"No submitted changes.": { ru: "Нет засабмиченных изменений.", de: "Keine übermittelten Änderungen.", fr: "Aucune modification envoyée.", es: "No hay cambios enviados." },
// history context menu
"Open this changelist": { ru: "Открыть этот changelist", de: "Diese Changelist öffnen", fr: "Ouvrir ce changelist", es: "Abrir este changelist" },
"Revert (undo) #{n}": { ru: "Откатить (revert) #{n}", de: "Zurücksetzen (undo) #{n}", fr: "Annuler (revert) #{n}", es: "Revertir (undo) #{n}" },
"Copy number #{n}": { ru: "Скопировать номер #{n}", de: "Nummer #{n} kopieren", fr: "Copier le numéro #{n}", es: "Copiar número #{n}" },
"Copy description": { ru: "Скопировать описание", de: "Beschreibung kopieren", fr: "Copier la description", es: "Copiar descripción" },
// preview
"Select a file on the left": { ru: "Выберите файл слева", de: "Wähle links eine Datei", fr: "Sélectionnez un fichier à gauche", es: "Selecciona un archivo a la izquierda" },
"Loading diff…": { ru: "Загрузка diff…", de: "Diff wird geladen…", fr: "Chargement du diff…", es: "Cargando diff…" },
"No text differences (or binary file).": { ru: "Нет текстовых отличий (или бинарный файл).", de: "Keine Textunterschiede (oder Binärdatei).", fr: "Aucune différence de texte (ou fichier binaire).", es: "Sin diferencias de texto (o archivo binario)." },
"Binary file — no code preview.": { ru: "Бинарный файл — превью кода нет.", de: "Binärdatei — keine Code-Vorschau.", fr: "Fichier binaire — pas daperçu du code.", es: "Archivo binario — sin vista previa de código." },
"File is too large to preview.": { ru: "Файл слишком большой для превью.", de: "Datei zu groß für die Vorschau.", fr: "Fichier trop volumineux pour laperçu.", es: "Archivo demasiado grande para previsualizar." },
"Diff": { ru: "Diff", de: "Diff", fr: "Diff", es: "Diff" },
"New file": { ru: "Новый файл", de: "Neue Datei", fr: "Nouveau fichier", es: "Archivo nuevo" },
"Text": { ru: "Текст", de: "Text", fr: "Texte", es: "Texto" },
"{n} lines": { ru: "{n} строк", de: "{n} Zeilen", fr: "{n} lignes", es: "{n} líneas" },
"Image": { ru: "Изображение", de: "Bild", fr: "Image", es: "Imagen" },
"Resolution": { ru: "Разрешение", de: "Auflösung", fr: "Résolution", es: "Resolución" },
"Format": { ru: "Формат", de: "Format", fr: "Format", es: "Formato" },
"Source": { ru: "Источник", de: "Quelle", fr: "Source", es: "Fuente" },
"working copy": { ru: "рабочая копия", de: "Arbeitskopie", fr: "copie de travail", es: "copia de trabajo" },
"Unreal · embedded thumbnail": { ru: "Unreal · вшитая миниатюра", de: "Unreal · eingebettetes Vorschaubild", fr: "Unreal · miniature intégrée", es: "Unreal · miniatura incrustada" },
"Reading asset…": { ru: "Читаю ассет…", de: "Asset wird gelesen…", fr: "Lecture de lasset…", es: "Leyendo asset…" },
"This asset has no embedded thumbnail. Interactive 3D works for FBX / OBJ / GLB / GLTF / STL — export the model to one of these formats.": { ru: "В этом ассете нет вшитой миниатюры. Интерактивный 3D работает для FBX / OBJ / GLB / GLTF / STL — экспортни модель в один из этих форматов.", de: "Dieses Asset hat kein eingebettetes Vorschaubild. Interaktives 3D funktioniert für FBX / OBJ / GLB / GLTF / STL — exportiere das Modell in eines dieser Formate.", fr: "Cet asset na pas de miniature intégrée. La 3D interactive fonctionne pour FBX / OBJ / GLB / GLTF / STL — exportez le modèle dans lun de ces formats.", es: "Este asset no tiene miniatura incrustada. El 3D interactivo funciona con FBX / OBJ / GLB / GLTF / STL — exporta el modelo a uno de estos formatos." },
"Type": { ru: "Тип", de: "Typ", fr: "Type", es: "Tipo" },
"Preview": { ru: "Превью", de: "Vorschau", fr: "Aperçu", es: "Vista previa" },
"thumbnail": { ru: "миниатюра", de: "Vorschaubild", fr: "miniature", es: "miniatura" },
"none": { ru: "нет", de: "keine", fr: "aucune", es: "ninguna" },
"Revision": { ru: "Ревизия", de: "Revision", fr: "Révision", es: "Revisión" },
// model viewer
"drag — rotate · wheel — zoom · right-click — pan": { ru: "тащи — вращать · колесо — зум · ПКМ — панорама", de: "ziehen — drehen · Rad — zoomen · Rechtsklick — schwenken", fr: "glisser — pivoter · molette — zoom · clic droit — panoramique", es: "arrastra — girar · rueda — zoom · clic derecho — panorámica" },
"Loading model…": { ru: "Загрузка модели…", de: "Modell wird geladen…", fr: "Chargement du modèle…", es: "Cargando modelo…" },
"Format not supported by the viewer": { ru: "Формат не поддерживается вьювером", de: "Format vom Viewer nicht unterstützt", fr: "Format non pris en charge par la visionneuse", es: "Formato no compatible con el visor" },
"Collada: empty scene": { ru: "Collada: пустая сцена", de: "Collada: leere Szene", fr: "Collada : scène vide", es: "Collada: escena vacía" },
"Triangles": { ru: "Треугольники", de: "Dreiecke", fr: "Triangles", es: "Triángulos" },
"Vertices": { ru: "Вершины", de: "Ecken", fr: "Sommets", es: "Vértices" },
"Meshes · Materials": { ru: "Меши · Материалы", de: "Meshes · Materialien", fr: "Maillages · Matériaux", es: "Mallas · Materiales" },
"Render": { ru: "Рендер", de: "Render", fr: "Rendu", es: "Render" },
"Wireframe": { ru: "Каркас", de: "Drahtgitter", fr: "Filaire", es: "Malla" },
"Grid": { ru: "Сетка", de: "Raster", fr: "Grille", es: "Cuadrícula" },
"Reset view": { ru: "Сбросить вид", de: "Ansicht zurücksetzen", fr: "Réinitialiser la vue", es: "Restablecer vista" },
// updater
"Update available": { ru: "Доступно обновление", de: "Update verfügbar", fr: "Mise à jour disponible", es: "Actualización disponible" },
"Later": { ru: "Позже", de: "Später", fr: "Plus tard", es: "Más tarde" },
"Downloading… {pct}%": { ru: "Загрузка… {pct}%", de: "Wird geladen… {pct}%", fr: "Téléchargement… {pct}%", es: "Descargando… {pct}%" },
"Installed — restarting…": { ru: "Установлено — перезапускаю…", de: "Installiert — Neustart…", fr: "Installé — redémarrage…", es: "Instalado — reiniciando…" },
"Update now": { ru: "Обновить сейчас", de: "Jetzt aktualisieren", fr: "Mettre à jour", es: "Actualizar ahora" },
};
export function t(en: string, vars?: Record<string, string | number>): string {
let s = D[en]?.[LANG] ?? en;
if (vars) for (const k in vars) s = s.split("{" + k + "}").join(String(vars[k]));
return s;
}