Skip to content

feat: add Russian (ru) localization - #143

Open
everysingletear wants to merge 4 commits into
donetick:developfrom
everysingletear:feat/russian-locale
Open

feat: add Russian (ru) localization#143
everysingletear wants to merge 4 commits into
donetick:developfrom
everysingletear:feat/russian-locale

Conversation

@everysingletear

Copy link
Copy Markdown

Adds a complete Russian translation as a new locale. No architecture changes — follows the existing i18n setup.

What's included

  • public/locales/ru/{common,chores,settings}.json — all three namespaces, keys mirror en 1:1 (30 / 71 / 147, verified programmatically).
  • AVAILABLE_LANGUAGES — adds { code: 'ru', name: 'Russian', nativeName: 'Русский' } to the selector.
  • import 'moment/locale/ru' in src/i18n/config.js (matching the existing moment/locale/ja) so Russian dates format correctly.

Translation notes

  • Terminology is kept consistent across all three files (e.g. chore → «дело», circle → «группа», due date → «срок»).
  • The English source mixes "Chore" and "Task" (completeChore: "Complete Task"); the Russian unifies both as «дело».
  • Created By is rendered gender-neutrally as «Автор» to avoid Russian past-tense gender agreement.
  • Technical tokens (webhook URL placeholder, Plus plan name, date-format patterns) are kept verbatim.

Verification

  • npm run build passes.
  • All three ru namespaces load at runtime (GET /locales/ru/*.json → 200) with the selector set to Russian; document.documentElement.lang becomes ru.
  • No i18next missing-key warnings.

First namespace of the Russian locale. Keys mirror en/common.json 1:1;
values follow the project glossary (chore=дело, points=очки, etc.).
Thing is rendered as "Штуки" to keep the deliberately casual tone of the
English "Things".
Second namespace. Keys mirror en/chores.json 1:1, values follow the
glossary (chore/task=дело, due date=срок, assignee=исполнитель,
subtask=подзадача). "Created By" is rendered gender-neutrally as "Автор"
to avoid Russian past-tense gender agreement.
Third and final namespace. Keys mirror en/settings.json 1:1. Circle=группа,
Notification=уведомление, task=дело. Technical tokens kept verbatim
(webhook URL placeholder, "Plus" plan name, date-format patterns).
Add the ru entry to the language selector and import moment/locale/ru so
Russian dates format correctly (matching the existing moment/locale/ja
pattern). The ru dictionaries already exist under public/locales/ru.
@meauxt

meauxt commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Thank for the contribtion! will hold on few days to see if anyone interested in review it or has any comments!

@everysingletear

Copy link
Copy Markdown
Author

No rush on the review — but I noticed crowdin.yml landed on develop in the meantime, with /public/locales/en/*.json as the source and update_as_unapproved for translations. That changes the question this PR is answering, so I'd rather ask than let it sit:

Do you still want ru as a direct PR, or should it come in through Crowdin instead?

Either way works for me:

  • Keep this PR — it's 220 hand-written keys across common/chores/settings, matching the en scaffold as it stood when I opened it. Merging it gives Crowdin a non-empty ru to start from instead of a cold 0%, and ru maps cleanly under two_letters_code with no addition to languages_mapping.
  • Close it and route through Crowdin — equally fine. I can upload the same values as a pre-translation so nothing is lost, once the project is accepting uploads.

The one thing I'd flag: ru is currently in AVAILABLE_LANGUAGES but has no files under public/locales/, so picking Russian today falls back to English. Whichever path you prefer, that gap is worth closing.

Tell me which you'd rather have and I'll do the legwork — no need for you to untangle it.

@everysingletear

Copy link
Copy Markdown
Author

Correction to my last paragraph: I had that backwards. ru is not in AVAILABLE_LANGUAGES on develop today — this PR is what adds it, alongside the locale files, so the list and the files land together and there's no pre-existing fallback gap. Sorry for the noise.

The actual question stands: direct PR, or route ru through Crowdin now that it's set up?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants