iOS-приложение для перевода текста через Google Gemini API. Пользователь настраивает API-ключ и параметры перевода один раз, затем может переводить текст из любого приложения через Share-меню (Action Extension) или через буфер обмена.
3 таргета:
- Transcriptor — основное приложение (настройки, ручной перевод, история)
- TranscriptorAction — Action Extension для перевода из любого приложения через Share
- TranscriptorKit — shared framework (сервисы, модели, утилиты)
Связь: App Groups + Keychain Sharing + embedded framework.
- iOS 26+, universal (iPhone + iPad)
- SwiftUI, Swift Concurrency (async/await, actors)
- SwiftData (история переводов)
- URLSession + Gemini REST API (JSON mode)
- Ноль сторонних зависимостей
- Bundle ID:
com.claustrofob.Transcriptor - Extension Bundle ID:
com.claustrofob.Transcriptor.Action - App Group:
group.com.claustrofob.Transcriptor
- GitHub:
claustrofob/Transcriptor(private) - SSH настроен
- Коммиты:
git add+git commitлокально - Push/Pull:
git push/git pullчерез SSH - Единственный контрибьютор — конфликтов нет, можно пушить напрямую в main
Translate:
- Поле для ввода текста + кнопка старта перевода
- Ниже появляется текст перевода в отформатированном виде + кнопка копирования
- Указывается оригинальный язык текста
- Форматирование зависит от настройки "Detail Level":
- Concise — нормальная форма + род/тип, значения через запятую, контекст использования
- Standard — всё из Concise + ненумерованный список примеров (оригинал — перевод)
- Detailed — всё из Concise + нумерованный список значений, каждое с ненумерованными примерами
History:
- Список последних 50 переведённых слов/фраз. Более старые удаляются из базы
- В списке видно: оригинальное слово, точный перевод (без форматирования), язык перевода
- Вся остальная информация открывается по тапу
Settings:
- Google AI API Key
- Target Language
- Model
- Detail Level
- About
Один запрос к Gemini API через generateContent с response_mime_type: "application/json" + response_schema.
Возвращает все данные сразу: перевод, метаданные, примеры.
Метод: GeminiService.translateFull(text:settings:) -> TranslationMeta
JSON Schema ответа:
{
"normalForm": "podekscytowany",
"synonyms": "podniecony, rozentuzjazmowany, ożywiony",
"translatedText": "прилагательное (м.р.)\nвзволнованный, возбуждённый...",
"sourceLanguage": "Польский (pl)",
"shortTranslation": "взволнованный, возбуждённый",
"examples": [
{
"original": "Jestem podekscytowany tą wiadomością.",
"translation": "Я взволнован этой новостью."
}
]
}Поля:
normalForm(string, required) — словарная (начальная) форма слова на языке оригинала. Существительное → им.п. ед.ч., прилагательное → им.п. ед.ч. м.р., глагол → инфинитивsynonyms(string, required) — синонимы на языке оригинала через запятую. Пустая строка если нет синонимовtranslatedText(string, required) — полный текст перевода, plain text (нумерованные/ненумерованные списки через \n, без Markdown)sourceLanguage(string, required) — язык оригинала на target language + ISO 639-1 код в скобкахshortTranslation(string, required) — краткий перевод через запятую, plain text (без Markdown)examples(array, required) — примеры использования:- Concise: пустой массив
[] - Standard: 3-5 примеров
- Detailed: 5-8 примеров
- Каждый элемент:
{ "original": string, "translation": string }
- Concise: пустой массив
TranslationContentView (TranscriptorKit) — переиспользуемая view для отображения контента перевода:
- Source language badge (опционально)
- Нормальная форма + синонимы
- Текст перевода (plain Text)
- Список примеров (ExamplesView), отделённый Divider
CopyTranslationButton (TranscriptorKit) — кнопка копирования с анимацией "Copied!":
- Параметры: text, label (default "Copy"), fullWidth (default false)
- fullWidth: true — bordered стиль на всю ширину, зелёный tint при копировании
- fullWidth: false — компактная кнопка для toolbar
Обе view используются в: TranslationView, ActionView, HistoryDetailView.
- Собрать и запустить на симуляторе — основное приложение должно открыться
- Ввести API key в Settings, проверить что сохраняется (перезапуск приложения)
- Ввести текст в TranslationView → нажать Translate → перевод появляется целиком
- Проверить историю: перевод появляется в History
- На реальном устройстве: выделить текст в Safari/Notes → Share → Transcriptor → перевод в модальном окне
- Clipboard: скопировать текст → переключиться в Transcriptor → предложение перевести