Полное руководство по созданию приложения-клона Google Translate с нуля

Полное руководство по созданию приложения-клона Google Translate с нуля

Добрый день! Я действительно взволнован проектом, который мне удалось получить для вас. Я сделал всё возможное, чтобы представить вам что-то с почти полной функциональностью, что-то, что вы можете продемонстрировать в своём портфолио, если вы новичок, и не волнуйтесь, я начну создавать некоторые проекты для разработчиков среднего и продвинутого уровня, просто имейте немного терпения, пока я сосредоточен на новых разработчиках .NET.

Сегодняшний проект является клоном приложения Google Translate с использованием Google translate API v2. Я эстет, поэтому сегодня я представлю вам красивое приложение, однако оно будет не только «красивым», но также будет переводить текст, определять язык на основе пользовательского ввода, отображать поддерживаемые языки и менять местами пользовательский ввод с результатом перевода. Так что, если вы так же взволнованы, как и я, давайте сделаем это!

Настройка Google Translate API ⚒

Во-первых, вам нужно выполнить несколько шагов, чтобы иметь возможность использовать Google Translate API.

  • Создайте учётную запись Google или используйте существующую, если таковая имеется
  • Получите доступ к облаку Google
  • Перейдите в облачную консоль Google.
  • Создайте новый проект и назовите его как хотите (мой был назван Google Translate Clone)
  • Получите доступ и найдите Cloud Translation API
  • Включите API, и вас попросит подключить платёжную учётную запись.
  • Также включите платёжную учётную запись (не волнуйтесь, мы не потратим ни копейки на этот проект)
  • В представлении проекта перейдите к APIs and services>Credentials
  • Выберите Create Credentials, а затем выберите API Key

После того, как вы выполнили эти шаги, вы можете начать использовать свой ключ API для этого и многих других проектов.

Если по какой-то причине вам не удалось выполнить шаги и вам нужна дополнительная помощь, вы можете посмотреть это видео , где автор выполняет те же шаги, которые я перечислил выше, но вы можете увидеть интерфейс Google Cloud и оценить его более визуально.

Альтернатива доступу к Google Cloud 🌩

Если вам не нравится идея доступа к Google Cloud и включения платёжной учётной записи, вы можете получить доступ к Rapid API , который является рынком API, где вы можете найти множество API для использования. Вам нужно будет создать учётную запись, а затем найти Google Translate API и подписаться бесплатно, после этого у вас будет доступ к учётным данным, которые Rapid API предоставляет, без необходимости доступа к Google Cloud или включения платёжной учетной записи. Не беспокойтесь, Rapid API поможет вам и фактически предоставит вам пример вызова, чтобы вы знали, как вызывать API с их хоста, и это будет выглядеть примерно так:

var client = new HttpClient(); var request = new HttpRequestMessage { Method = HttpMethod.Post, RequestUri = new Uri("<https://google-translate1.p.rapidapi.com/language/translate/v2/detect>"), Headers = { { "X-RapidAPI-Key", "YOUR_API_KEY" }, { "X-RapidAPI-Host", "google-translate1.p.rapidapi.com" }, }, Content = new FormUrlEncodedContent(new Dictionary<string, string> { { "q", "English is hard, but detectably so" }, }), }; using (var response = await client.SendAsync(request)) { response.EnsureSuccessStatusCode(); var body = await response.Content.ReadAsStringAsync(); Console.WriteLine(body); }

Создайте новое приложение Blazor WebAssembly 👨🏾‍💻

Теперь, когда у вас есть доступ к Google Translate API, давайте начнём создавать новое приложение blazor wasm с помощью dotnet cli.

dotnet new blazorwasm-empty -o GoogleTranslateClone

Как всегда, перейдите в свой файл wwwroot>Index.html и раскомментируйте свою таблицу стилей CSS с ограниченной областью действия, поскольку по умолчанию эта строка всегда комментируется, и вы должны включить её самостоятельно.

<link href="GoogleTranslateClone.styles.css" rel="stylesheet" />

Добавьте модели для хранения данных 📦

Нам нужны некоторые модели для хранения данных, которые мы получаем от вызовов веб-API, поэтому давайте добавим папку с именем Models и создадим следующие классы моделей.

public class Language { [JsonPropertyName("language")] public string language { get; set; } [JsonPropertyName("name")] { public string name { get; set; } } public class TranslatedText { [JsonPropertyName("translatedText")] public string translatedText { get; set; } }

Мы добавили модель Language для хранения коллекции поддерживаемых языков из API и класс TranslatedText для хранения ответа от конечной точки перевода. Мы используем JsonpropertyName для сопоставления ответа JSON со свойствами нашей модели при десериализации.

Создайте класс GoogleTranslateClient 🈸

Поскольку мы вызываем веб-API, нам нужен клиентский класс, который использует его конечные точки, поэтому давайте добавим новую папку с именем Client и внутри неё мы создадим класс GoogleTranslateClient.

public class GoogleTranslateClient { private const string baseAddress = "<https://translation.googleapis.com/language/translate/v2>"; private readonly HttpClient client; public GoogleTranslateClient(HttpClient client) { this.client = client; } }

Мы начинаем с добавления константы, которая будет содержать базовый адрес клиента, который является адресом Google API. После этого мы добавляем конструктор и передаём в качестве параметра HttpClient для создания экземпляра клиента посредством внедрения зависимостей в класс Program.

Это еще не всё, но я не хочу бросать вам кучу строк кода и ожидать, что вы всё это так легко поймёте. Поэтому я объясню каждый метод клиента отдельно.

Метод GetSupportedLanguages

С помощью этого метода, как следует из названия, мы собираемся получить набор языков, поддерживаемых Google API.

public async Task<Dictionary<string,Language>> GetSupportedLanguages() { var queryParams = new Dictionary<string, string>() { ["key"] = "YOUR_API_KEY", ["target"] = "en" }; var uri = QueryHelpers.AddQueryString($"{baseAddress}/languages", queryParams); var response = await client.GetAsync(uri); string content = await response.Content.ReadAsStringAsync(); var data = JsonObject.Parse(content); var languagesNode = data["data"]["languages"]; var languages = JsonSerializer.Deserialize<List<Language>>(languagesNode) .ToDictionary(x => x.language); return languages; }

Ничего особенного или необычного, мы начинаем с создания строкового словаря для хранения ключа API и целевого языка. Мы используем класс QueryHelpers, чтобы легко добавить строку запроса, просто передав адрес и параметры запроса в качестве параметров, но для использования класса QueryHelpers вам необходимо установить библиотеку Microsoft, поэтому подготовьте свой терминал для использования dotnet cli.

dotnet add package Microsoft.AspNetCore.WebUtilities

Теперь вы сможете использовать его без каких-либо ошибок. Затем мы отправляем запрос и анализируем его в строку, как всегда. А теперь хакерские вещи, чтобы не создавать кучу моделей для получения данных языков, я работаю напрямую с узлами JSON и погружаюсь в его свойства, чтобы языки, наконец, просто десериализовали в список и вызывали метод ToDictionary для возврата словаря

Метод DetectLanguage

Вы когда-нибудь находили фрагмент текста, который хотели бы перевести, но не знали, на каком языке он написан? Что делает этот метод, так это определяет язык на основе ввода пользователя.

public async Task<Language> DetectLanguage(string input) { var queryParams = new Dictionary<string, string>() { ["key"] = "YOUR_API_KEY", ["q"] = input }; var uri = QueryHelpers.AddQueryString($"{baseAddress}/detect", queryParams); var response = await client.PostAsync(uri, null); var content = await response.Content.ReadAsStringAsync(); JsonNode data = JsonNode.Parse(content); JsonNode languageNode = data["data"]["detections"][0][0]; Language language = JsonSerializer.Deserialize<Language>(languageNode); return language; }

Мы начинаем создавать тот же словарь строк для хранения ключа API, и в этом случае мы передаём параметр q, который является вводом пользователя для анализа и определения исходного языка. Всё то же самое, мы вызываем класс QueryHelpers для передачи строк запроса, отправки почтового запроса и чтения ответа в виде строки для анализа в JsonNode, чтобы вручную манипулировать данными узла и десериализовать в объект Language и переходить к его возврату.

Метод Translate

Наконец, метод перевода ввода от клиента на нужный язык.

public async Task<TranslatedText> Translate(string input, string source, string target) { var queryParams = new Dictionary<string, string>() { ["key"] = "YOUR_API_KEY", ["q"] = input, ["target"] = target, ["source"] = source }; var uri = QueryHelpers.AddQueryString(baseAddress, queryParams); var response = await client.PostAsync(uri, null); var content = await response.Content.ReadAsStringAsync(); JsonNode data = JsonNode.Parse(content); JsonNode translateNode = data["data"]["translations"][0]; TranslatedText translation = JsonSerializer.Deserialize<TranslatedText>(translateNode); return translation; }

Этот метод принимает три параметра: входные данные — переводимое предложение, язык, на котором написано предложение, и целевой язык для перевода.

Мы передаём эти три параметра как строки запроса в словарь queryParams и используем класс QueryHelpers для передачи параметров запроса в запрос. Мы выполняем почтовый запрос и анализируем ответ на строку, чтобы, наконец, выполнить анализ в узле JSON, для получения доступа к его свойствам и получения переведённого текста.

Это всё GoogleTranslateClient. поэтому давайте добавим его в коллекцию сервисов в классе Program.

builder.Services.AddScoped<GoogleTranslateClient>();

Добавление разметки в файл Index.razor✔

Это наш основной файл, поэтому мы добавим сюда всю разметку. Мы не будем создавать никаких компонентов, хотя мы могли бы это сделать, так как у нас будет повторяющаяся разметка для этого примера. Мы сделали всё это в одном файле без компонентов, но вы определённо можете реализовать компоненты и улучшить приложение во многих отношениях.

<div class="container"> <h1>Blazor Translate</h1> <div class="wrapper"> <ul class="controls"> <li class="row from"> <select name="language-select" title="language-select" @bind="@sourceLanguage"> <option value="">Detect Language</option> @foreach (var language in supportedLanguages) { <option value="@language.Key">@language.Value.name</option> } </select> </li> <li class="exchange" @onclick="@ChangeSourceToTarget"><i class="fas fa-exchange-alt"></i></li> <li class="row to"> <select name="language-select" title="language-select" @bind="@targetLanguage"> @foreach (var language in supportedLanguages) { <option value="@language.Key">@language.Value.name</option> } </select> </li> </ul> <div class="text-input"> <textarea class="from-text" name="" id="" cols="30" rows="10" placeholder="Enter Text" @bind="@sourceText"></textarea> <textarea class="to-text" name="" id="" cols="30" rows="10" readonly disabled placeholder="Translation" @bind="@translatedText"></textarea> </div> </div> <div class="detected-text"> @if (!string.IsNullOrEmpty(notificationText)) { <p>@notificationText</p> } </div> <button @onclick="@Translate">Translate Text</button> </div>

Мы определяем некоторую базовую HTML-разметку, но есть некоторые аспекты, на которые я хотел бы указать. Например на то, что мы заполняем элементы управления выбором, используя метод поддерживаемых языков, используя цикл foreach для создания тега параметра для каждого поддерживаемого языка.

Кроме того, мы добавили кнопку для замены исходного текста целевым текстом, чтобы поменять местами контент для перевода с переведённым контентом.

Есть два элемента управления textarea для ввода текста для перевода и отображения перевода. Ниже у нас есть уведомление для отображения некоторых сообщений, например, для ввода действительного текста, если текстовая область пуста, или для уведомления пользователя, когда текст переведён и, наконец, кнопка для выполнения перевода.

Добавление кода в файл Index.razor🪒

@code { private string sourceLanguage; private string targetLanguage = "en"; private string sourceText; private string translatedText; private string notificationText; private Dictionary<string, Language> supportedLanguages = new Dictionary<string, Language>(); protected override async Task OnInitializedAsync() { supportedLanguages = await GetSupportedLanguages(); } private async Task<Dictionary<string, Language>> GetSupportedLanguages() => await client.GetSupportedLanguages(); private async Task<Language> DetectLanguage(string input) => await client.DetectLanguage(input); private async Task Translate() { notificationText = string.Empty; if(string.IsNullOrEmpty(sourceText)) { notificationText = "Insert a valid text to translate"; } else if (string.IsNullOrEmpty(sourceLanguage)) { Language detectedLanguage = await DetectLanguage(sourceText); var translationResult = await client.Translate(sourceText, detectedLanguage.language, targetLanguage); translatedText = translationResult.translatedText; if (supportedLanguages.TryGetValue(detectedLanguage.language, out Language language)) { notificationText = $"Detected from {language.name}"; } } else { var translationResult = await client.Translate(sourceText, sourceLanguage, targetLanguage); string decodedString = HttpUtility.HtmlDecode(translationResult.translatedText); translatedText = decodedString; } } private void ChangeSourceToTarget() { string tempSource = ""; string temptext = ""; tempSource = sourceLanguage; temptext = sourceText; if (string.IsNullOrEmpty(tempSource)) { tempSource = "en"; } sourceLanguage = targetLanguage; sourceText = translatedText; targetLanguage = tempSource; translatedText = temptext; } }

Вверху мы определяем несколько полей для хранения значений элементов управления выбора и текстовых областей, а также инициализируем свойство SupportedLanguages и заполняем его в методе жизненного цикла OnInitializedAsync с помощью метода GetSupportedLanguages, который, как мы помним, возвращает словарь, содержащий поддерживаемые языки.

Метод определения языка вызывает клиентский метод определения языка и возвращает код языка. Теперь почти всё волшебство происходит в методе перевода, мы устанавливаем поле notificationText пустым, поскольку мы хотим очистить любое старое сообщение. Затем мы проверяем, не пусто ли свойство sourceText (то есть текст для перевода), если это так, то мы отображаем сообщение об ошибке. Затем мы приступаем к проверке, если sourceLanguage пуст (это язык текста, который мы хотим перевести), то это означает, что мы хотим определить язык, поэтому мы вызываем функцию обнаружения языка и возвращаем язык, на котором он написан. Далее нам нужно перевести текст, декодировать ответ, поскольку ответ содержит некоторые HTML-коды, и, наконец, мы проверяем словарь поддерживаемых языков, чтобы найти имя обнаруженного языка, поскольку функция обнаружения языка возвращает код языка (например, en, es, de, fr), но не фактическое имя. Поэтому, как только мы узнаем язык, мы выводим уведомление, отображающее язык, с которого был переведён текст. Следуя последнему случаю условного оператора, если оба аргумента sourceLanguageиsourceText не пусты, то мы переходим к обычному переводу без каких-либо дополнительных задач.

Последний метод на самом деле является служебным методом для замены левой стороны приложения перевода правой стороной, поэтому для этого мы определяем некоторые временные переменные и начинаем обменивать значения. У нас есть условный оператор, проверяющий, был ли элемент управления выбора с левой стороны пустым. Затем мы изменим его значение на «en», это потому, что мы не можем отправить пустое значение, так как targetLanguage вызовет ошибку, потому что нам нужно всегда знать целевой язык.

Добавляем стиль в приложение 🕺🏾

Приложение готово к работе, но оно всё ещё выглядит уродливо, поэтому мы начнём добавлять некоторые стили. Давайте начнём с добавления ссылки на стиль в файл index.html, чтобы использовать шрифт awesome для добавления некоторых значков.

<link rel="stylesheet" href="<https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css>" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />

Добавление стиля к элементу body в app.css

Давайте перейдём к нашей глобальной таблице стилей CSS и добавим немного стилей:

body { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: #3A4454; }

Стиль Index.razor

Теперь давайте добавим основной стиль на нашу страницу razor. Для этого вам нужно создать новый файл CSS с именем, Index.razor.css, чтобы как только вы это сделали, вы могли добавить свой стиль с ограниченной областью действия.

* { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } .container { width: 100%; background: #fff; border-radius: 10px; padding: 30px; text-align: center; } .wrapper { border-radius: 5px; border: 1px solid #bbb; } .wrapper .text-input { display: flex; } .text-input .to-text { border-left: 1px solid #bbb; border-radius: 0px; background-color: #f7f7f7; } .text-input textarea { height: 250px; width: 100%; border: none; outline: none; resize: none; font-size: 30px; border-radius: 5px; background: none; padding: 10px 15px; } textarea::placeholder { font-size: 30px; color: #878787; font-weight: 400; } .controls , li{ display: flex; align-items: center; justify-content: space-between; } .controls, li, .icons , .icons i{ display: flex; align-items: center; justify-content: space-around; } .controls { list-style: none; padding: 12px 15px; border-bottom: 1px solid #bbb; } .controls .row select { border: none; outline: none; font-size: 18px; background: none; } select { cursor: pointer; } .controls .exchange { color: #9f9f9f; font-size: 16px; cursor:pointer; } .exchange { border-radius: 50%; border: 1px solid #ddd; padding: 15px; } .detected-text { text-align: start; margin-left: 10px; } .container button { width: 100%; padding: 14px; margin-top: 20px; border: none; outline: none; cursor: pointer; border-radius: 5px; font-size: 17px; background-color: #007bff; color: #fff; }

Тестирование вашего приложения 🧪

Вот и всё! Теперь у вас должен быть полностью работающий клон Google Translate. Как я говорил вам в начале, вы можете определять языки, получать коллекцию языков, поддерживаемых API, и переводить предложения с одного языка на другой. Приложение не идеально и может иметь некоторые улучшения, но я получил массу удовольствия от его создания и я надеюсь, что у вас тоже всё получилось!

Посмотрите репозиторий на GitHub 🐙

Если у вас возникли проблемы или у меня была опечатка, вы можете проверить репозиторий на GitHub.

Смотрите живую версию 🚀

Существует также живая версия этого приложения, размещённая бесплатно в виде статического веб-приложения Azure, которое вы можете проверить здесь.

Начать дискуссию