diff --git a/README.RU.md b/README.RU.md index 6d6db74..2d5a9c0 100644 --- a/README.RU.md +++ b/README.RU.md @@ -385,16 +385,19 @@ dotnet build Packaging\Installer\CursorLang.wixproj -p:SuppressValidation=false Partner Center не требует политику от приложения, которое не собирает данных, но страница, сказавшая это вслух, снимает вопрос у проверяющего. -Всё это — пять файлов в `site`, и ничего больше ниоткуда не подгружается: ни -фреймворка, ни сборки, ни шрифта из сети. +Ничего больше ниоткуда не подгружается: ни фреймворка, ни сборки, ни шрифта из +сети. | Файл | Что внутри | |---|---| -| `index.html` | Разметка, оба языка сразу | +| `index.html` | Английская страница — разметка, оба языка сразу | +| `ru/index.html` | Та же разметка, впереди по умолчанию — русский | | `styles.css` | Все правила, включая палитру | | `boot.js` | Язык и тема прошлого визита — до первого кадра | | `main.js` | Остальное: переключатели, меню, демонстрация, предпросмотр | | `favicon.svg` | Та же тёмная плашка с «Aя», что и у иконки приложения | +| `robots.txt` | Указывает краулерам на sitemap, ничего не запрещает | +| `sitemap.xml` | Обе страницы, связанные между собой через `hreflang` | `boot.js` вынесен отдельно и мал по одной причине: он должен отработать до разбора `body`. Он читает выбранное в прошлый раз и ставит это на элемент @@ -402,21 +405,24 @@ Partner Center не требует политику от приложения, на глазах исправлялся бы. `main.js` работает по разметке, которая к тому времени должна существовать, поэтому он отложенный. -Оба языка живут в разметке одновременно, а переключатель в углу выбирает один. -Каждый переведённый кусок — это `` рядом с ``, и -тот, который не совпадает с языком на элементе ``, скрывается правилом в -две строки. С выключенными скриптами страница всё равно читается — по-английски. +Оба языка живут в разметке каждой из страниц одновременно, а переключатель в +углу выбирает один. Каждый переведённый кусок — это `` рядом с +``, и тот, который не совпадает с языком на элементе ``, +скрывается правилом в две строки. С выключенными скриптами любая из страниц +всё равно читается — на том языке, что у неё по умолчанию. -Выкладка сводится к копированию этих пяти файлов как есть. До неё адреса ждут -две вещи. +Английский и русский — это две страницы, а не одна с переключателем на клиенте, +чтобы каждая могла ранжироваться в поиске сама по себе: `/` и `/ru/`, со +ссылками `hreflang` друг на друга и в разметке, и в `sitemap.xml`, и у каждой — +свой заголовок, описание и canonical. Клик по EN или RU — это настоящий переход +между ними, а не перерисовка на месте, а `boot.js` отправляет посетителя, +зашедшего впервые, на страницу под язык его браузера — но никогда не уводит с +той из двух, на которую уже привела ссылка: иначе раздельное ранжирование +пропадало бы для всех, кто пришёл именно так, включая краулеров. -Первая — страница в Store: обе кнопки загрузки сейчас не ссылки, а -``, потому что заглушка в `href` была бы -относительным адресом и всякий, кто по ней пойдёт, получил бы 404. С адресом -каждая возвращается в ``, а примечание рядом уходит. - -Вторая — адрес самого сайта: его ждут три тега, которых в шапке документа пока -нет, — `og:image`, `og:url` и canonical. +Выкладка сводится к копированию файлов как есть — на общие файлы (`/styles.css` +и другие) ссылаются по абсолютному пути, так что `ru/index.html` обращается к +той же копии, не заводя свою. Поддержка — `support@alrakis.kz`: адрес стоит в разделе о данных, под кнопками загрузки и в подвале. diff --git a/README.md b/README.md index a5b0072..708ee0e 100644 --- a/README.md +++ b/README.md @@ -378,16 +378,19 @@ does, and a privacy policy saying that it collects nothing. Partner Center leave the policy optional for an app that gathers no data, but a page saying so out loud is what takes the question off the reviewer's desk. -The whole of it is five files in `site`, and nothing is fetched from anywhere -else — no framework, no build step, no font from a network: +Nothing is fetched from anywhere else — no framework, no build step, no font +from a network: | File | What is in it | |---|---| -| `index.html` | The markup, both languages at once | +| `index.html` | The English page — the markup, both languages at once | +| `ru/index.html` | The same markup, Russian in front by default | | `styles.css` | Every rule, the palette included | | `boot.js` | The language and the theme of an earlier visit, before the first frame | | `main.js` | The rest: the switches, the menu, the demo, the preview | | `favicon.svg` | The same dark plate with "Aя" as the application icon | +| `robots.txt` | Points crawlers at the sitemap, nothing disallowed | +| `sitemap.xml` | Both pages, cross-linked by `hreflang` | `boot.js` is small and separate for one reason: it has to run before the body is parsed. It reads what was chosen last time and puts it on the `` element, @@ -395,22 +398,25 @@ and without it the first frame would be English in the system theme and correct itself a moment later, in plain sight. `main.js` works on markup that has to exist by then, so it is deferred instead. -Both languages live in the markup at once, and a switch in the corner picks one. -Every translated piece is a `` beside a ``, and -whichever does not match the language on the `` element is hidden by a rule -of two lines. With the scripts switched off the page still reads, in English. +Both languages live in the markup of each page at once, and a switch in the +corner picks one. Every translated piece is a `` beside a +``, and whichever does not match the language on the `` +element is hidden by a rule of two lines. With the scripts switched off either +page still reads, in the language it defaults to. -Publishing means copying the five files as they are. Before that, two things are -still waiting for an address. +English and Russian are two pages rather than one page with a client-side +switch, so that each can rank in search on its own: `/` and `/ru/`, with +`hreflang` links between them in both the markup and `sitemap.xml`, and each +carrying its own title, description and canonical address. Clicking EN or RU is +a real navigation between the two rather than a rewrite in place, and `boot.js` +sends a first-time visitor to the page matching their browser's language — +but never away from whichever of the two a link already pointed them at, since +that would undo the separate ranking for anyone arriving that way, crawlers +included. -The Store listing is the first: the two download buttons are -`` rather than links, because a -placeholder in `href` would be a relative address and anything following it would -land on a 404. The address turns each of them back into -`` and takes the note beside it away. - -The address of the site itself is the second, wanted by three tags the head does -not carry yet: `og:image`, `og:url` and the canonical link. +Publishing means copying the files as they are — assets are referenced by an +absolute path (`/styles.css` and so on) so `ru/index.html` reaches the same +copy rather than needing one of its own. Support goes to `support@alrakis.kz`: it stands in the privacy section, under the download buttons and in the footer. diff --git a/site/boot.js b/site/boot.js index b233146..bf18b7d 100644 --- a/site/boot.js +++ b/site/boot.js @@ -2,25 +2,39 @@ "use strict"; var root = document.documentElement; - var lang = null; + var onRuPage = location.pathname.indexOf("/ru/") === 0 || location.pathname === "/ru"; + var stored = null; var theme = null; try { - lang = localStorage.getItem("cursorlang.lang"); + stored = localStorage.getItem("cursorlang.lang"); theme = localStorage.getItem("cursorlang.theme"); } catch (error) { - // Storage refused: what the markup says stands + // Storage refused: what the URL says stands } - if (!lang && (navigator.language || "").toLowerCase().indexOf("ru") === 0) { - lang = "ru"; + // A stored choice always wins, on either page. + if (stored === "ru" && !onRuPage) { + location.replace("/ru/" + location.search + location.hash); + return; + } + if (stored === "en" && onRuPage) { + location.replace("/" + location.search + location.hash); + return; } - if (lang === "ru" || lang === "en") { - root.setAttribute("data-lang", lang); - root.setAttribute("lang", lang); + // No stored choice yet: send a browser set to Russian to the Russian page, + // once. Never redirect away from a page a link already pointed at — a + // shared /ru/ link, or a crawler fetching either URL directly, stands. + if (!stored && !onRuPage && (navigator.language || "").toLowerCase().indexOf("ru") === 0) { + location.replace("/ru/" + location.search + location.hash); + return; } + var lang = onRuPage ? "ru" : "en"; + root.setAttribute("data-lang", lang); + root.setAttribute("lang", lang); + if (theme === "dark" || theme === "light") { root.setAttribute("data-theme", theme); } diff --git a/site/index.html b/site/index.html index 936000a..bac521a 100644 --- a/site/index.html +++ b/site/index.html @@ -3,25 +3,57 @@ -CursorLang — the keyboard layout at your cursor +CursorLang — the keyboard layout at your cursor - + + + + + + + + + + + + - - + + + - - + + diff --git a/site/main.js b/site/main.js index 374de8f..c9f603b 100644 --- a/site/main.js +++ b/site/main.js @@ -11,10 +11,11 @@ /* ------------------------------------------------------- the language */ + var titleEl = document.querySelector("title"); var description = document.querySelector('meta[name="description"]'); - var TITLE_EN = document.title; - var TITLE_RU = "CursorLang — раскладка клавиатуры у курсора"; - var DESCRIPTION_EN = description.getAttribute("content"); + var TITLE_EN = titleEl.getAttribute("data-title-en"); + var TITLE_RU = titleEl.getAttribute("data-title-ru"); + var DESCRIPTION_EN = description.getAttribute("data-content-en"); var DESCRIPTION_RU = description.getAttribute("data-content-ru"); function applyLanguage(lang) { @@ -44,8 +45,15 @@ if (!button) { return; } var lang = button.getAttribute("data-set-lang"); - applyLanguage(lang); remember("cursorlang.lang", lang); + + // EN and RU live on separate URLs now, for search engines to rank + // independently — switching language means a real navigation. + var onRuPage = location.pathname.indexOf("/ru") === 0; + if (lang === "ru" && !onRuPage) { location.href = "/ru/"; return; } + if (lang === "en" && onRuPage) { location.href = "/"; return; } + + applyLanguage(lang); }); /* ---------------------------------------------------------- the theme */ diff --git a/site/og-image.png b/site/og-image.png new file mode 100644 index 0000000..d178964 --- /dev/null +++ b/site/og-image.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e56181dd32daf373af758fa7e657ecc425fabe0ceb0ab8255ffa39d58ae9ca8e +size 25365 diff --git a/site/robots.txt b/site/robots.txt new file mode 100644 index 0000000..4511448 --- /dev/null +++ b/site/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://cursor-lang.alrakis.kz/sitemap.xml diff --git a/site/ru/index.html b/site/ru/index.html new file mode 100644 index 0000000..d5061f1 --- /dev/null +++ b/site/ru/index.html @@ -0,0 +1,703 @@ + + + + + +CursorLang — раскладка клавиатуры у курсора + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + CursorLang +
+ + + +
+
+ + +
+ + + +
+
+
+ + + +
+ + +
+
+

+ Your layout — right where you type. + Раскладка — там, где вы печатаете. +

+ +

+ CursorLang is a lightweight Windows utility that lives in your system tray. Whenever you + switch your keyboard layout, a brief popup indicates the new language next to your text caret, + mouse pointer, or a fixed screen position. Half a second later, it fades away. + Simple and unobtrusive. + CursorLang — это легковесная утилита для Windows, которая находится в системном трее. + При смене раскладки клавиатуры рядом с курсором, указателем мыши или фиксированной точкой на экране + появляется короткое всплывающее окно, указывающее на новый язык. Через полсекунды оно исчезает. + Просто и ненавязчиво. +

+ + + +
+ FreeБесплатно + Windows 10 1809 and later, 64-bit + Windows 10 1809 и новее, 64 бита + Uses under 9 MB of memoryИспользует менее 9 МБ памяти + No internet connection required, zero telemetryБез доступа в сеть, без телеметрии. +
+ +
+
+ + +
+ + +
+ +
+
+ + +
+
+
+
+ + +
+
+

PositionПоложение

+

+ Three placement modes + Три режима размещения подсказки +

+

+ Choose your preferred placement in the settings. Each mode maintains its own independent + position, offset, and appearance. + Выберите желаемое положение в настройках. Каждый режим сохраняет свое независимое положение, + смещение и внешний вид. +

+ +
+
+
+ + + + + + RU + +
+

Near the mouse pointerРядом с указателем мыши

+

+ Position the indicator at any of the six sides or corners with a custom offset. + The popup dynamically follows your mouse pointer. + Разместите индикатор на любой из шести сторон или углов с заданным смещением. + Всплывающее окно динамически следует за указателем мыши. +

+
+ +
+
+ + + + + + RU + + +
+

Near the text caretРядом с текстовым курсором

+

+ Displays to the left or right of the caret, aligned with your current text line. + Отображается слева или справа от курсора, выравниваясь по текущей текстовой строке. +

+
+ +
+
+ + + + + + + + + + RU + +
+

A fixed point on the screenВ выбранной точке экрана

+

+ Choose from seven preset positions on your active monitor, complete with + customizable margins. The indicator stays pinned to a consistent spot, so you always + know where to look. + Выберите одно из семи предустановленных положений на активном мониторе с + настраиваемыми полями. Индикатор остается зафиксированным в одном и том же месте, + поэтому вы всегда будете знать, куда смотреть. +

+
+
+ +

+ Not every application exposes its text caret. Standard Win32 fields respond + instantly, while Chromium and Electron apps rely on accessibility APIs. However, applications + rendering text onto custom canvases—like certain tool windows in a JetBrains IDE—may not report + caret coordinates at all. + In these fallback scenarios, the indicator automatically defaults to your configured fixed point + placement, using that mode's appearance. It avoids jumping to the mouse pointer, as your eyes are + focused on typing and the mouse might be left anywhere on the screen. + Не всякое приложение сообщает, где его курсор. Стандартные поля Win32 реагируют + мгновенно, в то время как приложения Chromium и Electron используют API специальных возможностей. + Однако приложения, отображающие текст на пользовательских холстах — например, некоторые окна + инструментов в IDE JetBrains — могут вообще не сообщать координаты курсора. + В таких резервных сценариях индикатор автоматически переключается на заданное вами фиксированное + положение, используя внешний вид этого режима. Это позволяет избежать переключения на указатель мыши, + поскольку ваши глаза сосредоточены на наборе текста, а курсор может находиться в любой точке экрана. +

+
+
+ + +
+
+

AppearanceОформление

+

+ Each mode has its own unique look + У каждого режима своё оформление +

+

+ The caret popup lives right inside the text you're reading, keeping it compact and + unobtrusive. The fixed-screen popup, on the other hand, is meant to be larger and more prominent. + Therefore, offset, font size, opacity, and colors are saved independently for each mode — changing + one leaves the rest untouched. + Всплывающее окно с курсором располагается прямо внутри читаемого текста, оставаясь + компактным и ненавязчивым. Всплывающее окно с фиксированным экраном, напротив, предназначено для + большего размера и большей заметности. Поэтому смещение, размер шрифта, прозрачность и цвета + сохраняются независимо для каждого режима — изменение одного параметра не влияет на остальные. +

+ +
+
+

Layout popupПодсказка с раскладкой

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + Side of the caretСторона от курсора + +
+ + +
+
+ +
+ + +
+ + +
+ +
+
+ Sent you a Отправил тебе + pull requestRU +
+
+
+ +

+ The popup only answers a question you explicitly asked. It appears when you switch the + layout yourself. When you switch to another window, Windows may change the layout automatically in + the background, but the popup stays silent. After all, you didn't ask. + Всплывающее окно отвечает только на заданный вами вопрос. Оно появляется, когда вы + сами меняете расположение окон. При переключении на другое окно Windows может автоматически изменить + расположение окон в фоновом режиме, но всплывающее окно остается незамеченным. В конце концов, вы же + не спрашивали. +

+
+
+ + +
+
+

Caps Lock

+

+ A key that could be doing more + Клавиша, которая могла бы работать +

+

+ When enabled in settings, a quick tap of Caps Lock switches your keyboard layout instead + of toggling letter casing. Hold it longer than your custom threshold, and nothing changes: the popup + simply displays your current layout — a quick look, without switching. + Если эта функция включена в настройках, быстрое нажатие клавиши Caps Lock переключает + раскладку клавиатуры вместо изменения регистра букв. Если удерживать клавишу дольше заданного вами + порога, ничего не изменится: во всплывающем окне просто отобразится текущая раскладка — быстрый + взгляд без переключения. +

+ +
    +
  • + + + Disabled by default. This shortcut is disabled by default in the app, so + the Caps Lock key continues to function as usual. + По умолчанию выключено. В приложении Caps Lock отключен по умолчанию, + поэтому он продолжает работать так, как работал всегда. + +
  • +
  • + + + No administrator rights required. Built as a standard user-level + application, it runs under your active user account and simply doesn't need higher + privileges. + Права администратора не запрашиваются. Приложение разработано как + стандартное приложение для пользователей и работает под вашей активной учетной записью, + поэтому более высокие привилегии не требуются. + +
  • +
  • + + + Only one key is monitored. The low-level hook watches strictly for + Caps Lock. No other keystrokes are recorded, stored, or transmitted anywhere — and since + the app is offline, they physically can't be. + Отслеживается одна клавиша. Низкоуровневый перехватчик следит исключительно + за нажатием клавиши Caps Lock. Никакие другие нажатия клавиш не записываются, не сохраняются и + не передаются никуда — а поскольку приложение работает в автономном режиме, это + физически невозможно. + +
  • +
  • + + + Doesn't interfere with elevated apps. Windows blocks ordinary apps from + injecting shortcuts into windows running as administrator (like Task Manager, Registry Editor, + or UAC prompts). + Не работает в окнах, запущенных от администратора. + Windows блокирует возможность передачу нажатия клавиш в окнах, запущенные от имени + администратора, и служебных приложениях (например, диспетчером задач, редактором реестра + или запросами UAC). + +
  • + +
+
+
+ + +
+
+

LightweightЛегковесность

+

+ Small enough to forget about + Настолько маленькое, что о нём легко забыть +

+

+ CursorLang is split into two executables. The one that lives in your tray all day is + built without a UI rendering stack — a tooltip that Win32 draws in eight megabytes has no business + costing a hundred. Anything heavier is strictly kept out of the background process. The second + executable handles the settings: it loads only when you open the window and completely unloads + from memory the moment you close it. + CursorLang разделен на два исполняемых файла. Тот, что находится в системном трее весь + день, создан без стека рендеринга пользовательского интерфейса — всплывающая подсказка, которую + Win32 отрисовывает за восемь мегабайт, не должна стоить сто. Все, что потребляет больше ресурсов, + строго исключено из фонового процесса. Второй исполняемый файл обрабатывает настройки: он загружается + только при открытии окна и полностью выгружается из памяти в момент его закрытия. +

+ +
    +
  • + + + Under 9 MB in the tray. The settings window is a separate + process: it starts when you ask for it and gives its memory back when you + close it. + Меньше 9 МБ в трее. Окно настроек — отдельный процесс: он + запускается, когда его просят, и возвращает память, когда окно + закрывают. + +
  • +
  • + + + Starts with Windows if you ask it to — and Windows always keeps the final + word. Turn off startup in Settings → Apps → Startup, and the application respects your choice + instead of quietly re-enabling itself. + Запускается вместе с Windows, если попросить — и последнее слово всегда + остается за Windows. Отключите автозагрузку в Параметры → Приложения → Автозагрузка, и + приложение будет уважать ваш выбор, вместо того чтобы незаметно включаться снова. + +
  • +
  • + + + Light and dark themes, following Windows or pinned to one + of them. Available in English and Russian. + Светлая и тёмная тема, вслед за Windows или закреплённая. + Интерфейс — русский и английский. + +
  • +
  • + + + Nothing else to install. The application carries its own + copy of .NET, and the Microsoft Store keeps it up to date. + Ничего дополнительно устанавливать не нужно. Приложение несёт с собой + свою копию .NET, a обновляет его Microsoft Store. + +
  • +
+
+
+ + +
+
+

Privacy policyПолитика конфиденциальности

+

+ CursorLang collects nothing + CursorLang ничего не собирает +

+

+ It really is that simple. The points below just provide the technical details. + Всё действительно так просто. Приведённые ниже пункты содержат лишь технические подробности. +

+ +
+
    +
  • + + + No personal data is collected, stored remotely, or shared with anyone. + There are no accounts, no ads, and no analytics. + Персональные данные не собираются, не хранятся удаленно и не + передаются никому. Нет учетных записей, рекламы и аналитики. + +
  • +
  • + + + The application makes no network requests. It contains no networking + code, telemetry, or internal update checks — the Microsoft Store handles updates + automatically. + Приложение не обращается в сеть. Не содержит сетевого кода, телеметрии + или внутренних проверок обновлений — обновления обрабатываются Microsoft Store + автоматически. + +
  • +
  • + + + Settings are stored locally in a single file. The Store version keeps + this in the app's local data folder, which Windows removes entirely upon uninstallation — + leaving no trace behind. + Настройки хранятся локально в одном файле. В версии из магазина + приложений они хранятся в локальной папке данных приложения, которую Windows полностью + удаляет при деинсталляции, не оставляя никаких следов. + +
  • +
  • + + + The optional Caps Lock shortcut uses a low-level keyboard hook. + It monitors strictly the Caps Lock key and nothing else. Your keystrokes are never + recorded or transmitted, and the feature is disabled by default. + Горячая клавиша Caps Lock использует низкоуровневый + перехват клавиатуры. Она отслеживает исключительно нажатие клавиши Caps Lock и + ничего больше. Ваши нажатия клавиш никогда не записываются и не передаются, и эта + функция отсутствует по умолчанию. + +
  • +
  • + + + To position the popup, the app asks Windows for the active layout and + caret location. This data never leaves the app's memory, and your actual typed + text is never read or accessed. + Для позиционирования всплывающего окна приложение запрашивает у Windows + активную компоновку и положение курсора. Эти данные никогда не покидают память + приложения, и фактический введенный вами текст никогда не считывается и не используется. + +
  • +
+
+
+
+ + +
+
+

DownloadЗагрузка

+

+ One click from the Microsoft Store + Устанавливается из Microsoft Store в один клик +

+

+ CursorLang is distributed through the Microsoft Store, which installs it and keeps it + updated on its own — nothing else is asked of you in between. It runs on Windows 10 version 1809 + (build 17763) or later, 64-bit. + CursorLang распространяется через Microsoft Store: он же его устанавливает и сам держит + обновлённым — больше от вас ничего не требуется. Работает на Windows 10 версии 1809 + (сборка 17763) или новее, 64 бита. +

+ + + +

+ Something not working, or a question about the application? Write to + support@alrakis.kz and name the version in + the letter — it is in the title of the settings window. + Что-то не работает или есть вопрос о приложении? Напишите на + support@alrakis.kz и назовите в письме + версию — она есть в заголовке окна настроек. +

+
+
+ +
+ + + + + diff --git a/site/sitemap.xml b/site/sitemap.xml new file mode 100644 index 0000000..aa0bc00 --- /dev/null +++ b/site/sitemap.xml @@ -0,0 +1,20 @@ + + + + https://cursor-lang.alrakis.kz/ + + + + monthly + 1.0 + + + https://cursor-lang.alrakis.kz/ru/ + + + + monthly + 1.0 + +