Как перевести текст HTML-страницы?

163
23 февраля 2019, 18:40

Приложение выполняет парсинг HTML страницы. HTML страница имеет текст, картинки, таблицы и другое содержание. Язык текста на странице - "язык_1".

Предполагается:
- перейти по ссылке сайта (сайты будут разные);
- сохранить страницу в БД (поле_1)(Сохранить первую страницу сайта);
- перевести страницу(на язык "язык_2");
- сохранить страницу в БД (поле_2).

А как сделать чтобы сохранилась структура страницы?
Как сделать чтобы приложение вернуло HTML страницу на языке "язык_2"?

Т.е. сделать что-то, похожее на функцию перевода GoogleChrome: зашёл на любой сайт, и перевёл страницу на русский язык..

Answer 1

Самое простое, что приходит в голову - рекурсивно обойти все элементы, спуститься до уровня текстовых узлов, перевести каждый из них по отдельности и заменить его содержимое переводом. Допустим, имеем такой код для перевода строки через API Яндекса (взято отсюда):

using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json;
//Reference: System.Net.Http, Newtonsoft.Json
namespace TranslateTest
{
    class Translator
    {
        public static async Task<string> Translate(string s, string lang)
        {
            if (s.Length > 0)
            {                
                string content = "text=" + WebUtility.UrlEncode(s);
                var cnt = new StringContent(content, Encoding.UTF8, "application/x-www-form-urlencoded");
                HttpClient client = new HttpClient();
                var response = await client.PostAsync(
                    "https://translate.yandex.net/api/v1.5/tr.json/translate?lang=" + lang
                    + "&key=APIKEY",
                    cnt
                    );
                var stream = await response.Content.ReadAsStreamAsync();                
                using (var sr = new StreamReader(stream))
                {
                    string line;
                    if ((line = sr.ReadLine()) != null)
                    {
                        if ((int)response.StatusCode != 200) throw new Exception(line);
                        Translation translation;
                        translation = JsonConvert.DeserializeObject<Translation>(line);    
                        s = "";
                        foreach (string str in translation.text)
                        {
                            s += str;
                        }
                    }
                }

                return s;
            }
            else
                return "";
        }
    }
    class Translation
    {
        public string code { get; set; }
        public string lang { get; set; }
        public string[] text { get; set; }
    }
}

Тогда код для перевода HTML-документа будет выглядеть так (у меня используется MSHTML):

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
//Reference: COM -> Microsoft HTML Object Library
namespace TranslateTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }        
        private async void button1_Click(object sender, EventArgs e)
        {   
            string html;                
            html = System.IO.File.ReadAllText("c:\\test\\test.html");
            textBox2.Text = await TranslateDocument(html);
        }
        string lang = "en-ru";
        public async Task<string> TranslateDocument(string html)
        {                
            mshtml.HTMLDocument doc = null;
            mshtml.IHTMLDocument2 d2 = null;
            mshtml.IHTMLDocument3 d = null;
            mshtml.IHTMLElementCollection body = null;
            mshtml.IHTMLElement bodyelem = null;
            mshtml.IHTMLDOMNode bodynode = null;
            try
            {
                //грузим документ в парсер
                doc = new mshtml.HTMLDocument();
                d2 = (mshtml.IHTMLDocument2)doc;
                d2.write(html);
                //находим body
                d = (mshtml.IHTMLDocument3)doc;
                body = d.getElementsByTagName("body");
                if (body.length == 0) throw new Exception("Fatal error: HTML has no BODY tag!");
                bodyelem = body.item(0);                
                bodynode = bodyelem as mshtml.IHTMLDOMNode;
                //рекурсивно переводим все узлы элемента body
                foreach (var node in bodynode.childNodes)
                {
                    await TranslateNode(node);
                }
                return bodyelem.innerHTML;
            }
            finally
            {
                //освобождение ресурсов
                if (doc != null) Marshal.ReleaseComObject(doc);
                if (d2 != null) Marshal.ReleaseComObject(d2);
                if (d != null) Marshal.ReleaseComObject(d);
                if (body != null) Marshal.ReleaseComObject(body);
                if (bodyelem != null) Marshal.ReleaseComObject(bodyelem);
                if (bodynode != null) Marshal.ReleaseComObject(bodynode);
            }
        }  
        public async Task TranslateNode(mshtml.IHTMLDOMNode node)
        {
            string val = "";
            if (node.nodeType == 3) //text node
            {
                val = node.nodeValue;
                if (val.Trim().Length == 0) return; //пусто - нечего переводить
                var res = await Translator.Translate(val, lang); //переводим содержимое узла           
                node.nodeValue = res; //меняем содержимое на перевод
            }
            else //element node
            {
                //не переводим скрипты и CSS
                if (node.nodeName.ToLower() == "script" || node.nodeName.ToLower() == "style") return;
                //обход дочерних узлов
                foreach (mshtml.IHTMLDOMNode x in node.childNodes)
                {
                    await TranslateNode(x);
                }
            }                
        }
    }
}

Пример исходного текста и перевода:

<p><b>The Antarctic</b> is a polar region around the Earth's South Pole, opposite the Arctic region around the North Pole. The Antarctic comprises the continent of Antarctica and the island territories located on the Antarctic Plate. The Antarctic region include the ice shelves, waters, and island territories in the <i>Southern Ocean</i> situated south of the Antarctic Convergence, a zone approximately 32 to 48 km (20 to 30 mi) wide varying in latitude seasonally. The region covers some 20 percent of the Southern Hemisphere, of which 5.5 percent (14 million km2) is the surface area of the Antarctic continent itself. All of the land and ice shelves south of 60°S latitude are administered under the Antarctic Treaty System. Biogeographically, the Antarctic ecozone is one of eight ecozones of the Earth's land surface.</p>
<h2>Geography</h2>
<div>
<p>
The maritime part of the region constitutes the area of application of the international Convention for the Conservation of Antarctic Marine Living Resources (CCAMLR), where for technical reasons the Convention uses an approximation of the Convergence line by means of a line joining specified points along parallels of latitude and meridians of longitude. The implementation of the Convention is managed through an international Commission headquartered in Hobart, Australia, by an efficient system of annual fishing quotas, licenses and international inspectors on the fishing vessels, as well as satellite surveillance.</p>
<p>Most of the Antarctic region is situated south of 60°S latitude parallel, and is governed in accordance with the international legal regime of the Antarctic Treaty System. The Treaty area covers the continent itself and its immediately adjacent islands, as well as the archipelagos of the South Orkney Islands, South Shetland Islands, Peter I Island, Scott Island and Balleny Islands.</p>
<p>The islands situated between 60°S latitude parallel to the south and the Antarctic Convergence to the north, and their respective 200-nautical-mile (370 km) exclusive economic zones fall under the national jurisdiction of the countries that possess them: South Georgia and the South Sandwich Islands (United Kingdom; also an EU Overseas territory), Bouvet Island (Norway), and Heard and McDonald Islands (Australia).</p>
<p><b>Kerguelen Islands</b> (France; also an EU Overseas territory) are situated in the Antarctic Convergence area, while the Falkland Islands, Isla de los Estados, Hornos Island with Cape Horn, Diego Ramírez Islands, Campbell Island, Macquarie Island, Amsterdam and Saint Paul Islands, Crozet Islands, Prince Edward Islands, and Gough Island and Tristan da Cunha group remain north of the Convergence and thus outside the Antarctic region.</p>
</div>
<P><B>Антарктике</B> это полярные области вокруг Южного полюса Земли, противоположная Арктике, вокруг Северного полюса. Карта состоит из антарктического материка и островных территорий, расположенных на антарктической плиты. Антарктический регион включает шельфовые ледники, воды и островных территорий в <I>Южный Океан</I> расположенный к югу от антарктической конвергенции, зона примерно от 32 до 48 км (от 20 до 30 ми) широкий различающихся по широте сезонно. Регион охватывает около 20 процентов из Южного полушария, из которых 5,5 процента (14 млн. км2) площади Антарктического континента. Все земли и шельфовые ледники к югу от 60°южной широты применяются в рамках системы Договора об Антарктике. Биогеографически, экозона в Антарктике является одним из восьми экозонам земельных участков земной поверхности.</P>
<H2>География</H2>
<DIV>
<P>В морской части региона является область применения Международной конвенции по сохранению морских живых ресурсов Антарктики (АНТКОМ), где по техническим причинам в Конвенции используется аппроксимация сходимость линии с помощью линии, соединяющей указанные точки вдоль параллелей широты и меридианов долготы. Реализация конвенции осуществляется через международную комиссию со штаб-квартирой в Хобарте, Австралия, с помощью эффективной системы ежегодных квот, лицензий и международных инспекторов на рыболовных судах, а также спутниковое наблюдение.</P>
<P>Большинство Антарктический регион расположен к югу от 60°южной широты параллельно, и управляется в соответствии с международно-правовым режимом системы Договора об Антарктике. Области договора распространяется на континенте и прилегающих островах, а также архипелаги Южно-Оркнейские острова, Южные Шетландские острова, остров Петра I, Скотт островов и островов Баллени.</P>
<P>Острова, расположенные между 60°ю. ш параллельно Южной и Антарктической конвергенции на север, и их 200-мильной (370 км) исключительной экономической зоны находятся под национальной юрисдикцией стран, которые ими обладают: Южная Георгия и Южные Сандвичевы острова (Великобритания; также ЕС заморская территория), Остров Буве (Норвегия) и Острова Херд и Макдональд (Австралия).</P>
<P><B>Кергелен </B> (Франция; также ЕС заморская территория) находятся в антарктической конвергенции зона, а Фолклендские острова, Исла де лос Эстадос, Орнос на острове с мыса Горн, Диего-Рамирес острова, Кэмпбелл остров, остров Маккуори, Амстердам и Сен-Поль острова Крозе, Принс-Эдуард острова, и остров Гоф и Тристан-да-Кунья группы остаются Северная сходимости и, следовательно, за пределами Антарктики.</P></DIV>

Но недостаток в том, что данный метод генерирует слишком много мелких запросов к API, поэтому перевод большого документа будет длиться очень долго. Кроме того, качество перевода страдает, так как предложения будут дробиться на части и переводиться отдельно. Чтобы улучшить данный способ, необходимо придумать, как агрегировать запросы. Скорее всего, элемент, содержащий только инлайновые подэлементы (b, span и т.п.), нужно переводить как цельный кусок, но как провернуть это, сохранив форматирование, задача довольно сложная.

READ ALSO
symfony 3 вывод в форму ChoiceType (select) из базы данных

symfony 3 вывод в форму ChoiceType (select) из базы данных

Помогите, пожалуйстаДелаю скрипт, в котором можно добавлять пользователя, каждый пользователь имеет свою должность (таблица в базе positions)

171
Вывод вложенных списков из БД с помощью PHP

Вывод вложенных списков из БД с помощью PHP

Подскажите, пожалуйста, как можно решить следующую задачуЕсть таблица Категории товаров

174
Opencart, ocmod, как заменить несколько строк?

Opencart, ocmod, как заменить несколько строк?

Допустим есть такой код в файле theme/default/common/headerphp:

198
Ошибка при развертывании проекта на Symfony 2.8

Ошибка при развертывании проекта на Symfony 2.8

у меня linux mint, при попытке запустить проект выдает ошибку: This page isn’t working

175