mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
feat(i18n): add Russian (ru) locale
Full Russian catalog (11,154 keys, 1:1 with en, identical nesting) and every
locale wiring point the app declares, so ru is a first-class locale instead
of an English fallback:
- messages/ru.json, project.inlang/settings.json, i18n boot script
- i18n config / load-locale, format-locale (ru-RU), date-fns ru
- labs: parse-reference-range RANGE_PROSE for Russian lab sheets
("3,5 - 5,0", "от 3,5 до 5,0", "до 5,6", "не более 5,6")
- AI: citation-coverage normative stems, coach system prompt and insights
reply footer (Russian prompts must override the hard-coded
"Respond in English"), population-norm patterns
- safety: outbound-screen Russian imperative/dose/unit/causality bank
- mental health: PHQ-9, GAD-7, WHO-5 and SCI wording
verify-message-catalogs.mjs passes for all 10 locales.
This commit is contained in:
1 parent
577e43004f
commit
6b9d222643
24 files changed
+14551
-6
No files matched your search
+14098
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,18 @@
|
||||
{
|
||||
"$schema": "https://inlang.com/schema/project-settings",
|
||||
"baseLocale": "en",
|
||||
"locales": ["en", "de", "fr", "es", "it", "pl", "ko", "nl", "pt-BR"],
|
||||
"locales": [
|
||||
"en",
|
||||
"de",
|
||||
"fr",
|
||||
"es",
|
||||
"it",
|
||||
"pl",
|
||||
"ko",
|
||||
"nl",
|
||||
"pt-BR",
|
||||
"ru"
|
||||
],
|
||||
"modules": [
|
||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4.5.5/dist/index.js"
|
||||
],
|
||||
|
||||
@@ -28,7 +28,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = join(__dirname, "..");
|
||||
const outDir = join(repoRoot, "public", "i18n");
|
||||
|
||||
const LOCALES = ["de", "en", "fr", "es", "it", "pl", "ko", "nl", "pt-BR"];
|
||||
const LOCALES = ["de", "en", "fr", "es", "it", "pl", "ko", "nl", "pt-BR", "ru"];
|
||||
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { de, enUS, es, fr, it, ko, nl, pl, ptBR } from "date-fns/locale";
|
||||
import { de, enUS, es, fr, it, ko, nl, pl, ptBR, ru } from "date-fns/locale";
|
||||
import type { Locale as DateFnsLocale } from "date-fns";
|
||||
|
||||
import type { Locale } from "@/lib/i18n/config";
|
||||
@@ -27,6 +27,8 @@ export function resolveDateFnsLocale(locale: Locale): DateFnsLocale {
|
||||
return nl;
|
||||
case "pt-BR":
|
||||
return ptBR;
|
||||
case "ru":
|
||||
return ru;
|
||||
default:
|
||||
return enUS;
|
||||
}
|
||||
|
||||
@@ -178,6 +178,24 @@ const NORMATIVE_KEYWORDS: Record<Locale, readonly string[]> = {
|
||||
"superior a",
|
||||
"inferior a",
|
||||
],
|
||||
// Russian replies state a target or a reference interval; the stems carry
|
||||
// the case endings (целевой/целевого, референсный/референсного), and "выше"/
|
||||
// "ниже" are the comparatives a recommendation actually uses. ё folds to е.
|
||||
ru: [
|
||||
"целев",
|
||||
"цель",
|
||||
"должн",
|
||||
"следует",
|
||||
"рекоменд",
|
||||
"нормальн",
|
||||
"норма",
|
||||
"референс",
|
||||
"выше",
|
||||
"ниже",
|
||||
"больше",
|
||||
"меньше",
|
||||
"в пределах",
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ import { patterns as pl } from "./pl";
|
||||
import { patterns as nl } from "./nl";
|
||||
import { patterns as ptBR } from "./pt-br";
|
||||
import { patterns as ko } from "./ko";
|
||||
import { patterns as ru } from "./ru";
|
||||
|
||||
export const POPULATION_NORM_PATTERNS: Readonly<
|
||||
Record<Locale, readonly RegExp[]>
|
||||
@@ -28,4 +29,5 @@ export const POPULATION_NORM_PATTERNS: Readonly<
|
||||
nl,
|
||||
"pt-BR": ptBR,
|
||||
ko,
|
||||
ru,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/** Population-norm framing, ru. */
|
||||
export const patterns: readonly RegExp[] = [
|
||||
/\bнормальн\w*\s+(?:диапазон|предел|значени|границ)\w*/i,
|
||||
/\b(?:референсн|референс)\w*\s+(?:интервал|диапазон|значени|границ)\w*/i,
|
||||
/\b(?:большинств|все|многие)\s+(?:люд|взросл)\w*/i,
|
||||
/\b(?:взросл|люд)\w*[^.?!]{0,15}(?:обычно|как\s+правило|в\s+среднем)\b/i,
|
||||
/\b(?:врач|специалист|эксперт|рекомендаци)\w*[^.?!]{0,20}(?:говор|рекоменду|счита)\w*/i,
|
||||
/\b(?:общ|широк)\w*\s+(?:популяци|выборк)\w*/i,
|
||||
/\b(?:средн|типичн|здоров)\w*\s+(?:показател|уровень|значени|норм)\w*/i,
|
||||
/\bв\s+среднем\s+по\s+популяции\b/i,
|
||||
];
|
||||
@@ -1258,6 +1258,7 @@ const LOCALE_REPLY_FOOTER_FALLBACK: Record<
|
||||
pl: "\n\nREPLY LANGUAGE: respond in Polish. Mirror the user's register (formal Pan/Pani for medical-adjacent topics); use natural Polish health vocabulary.",
|
||||
nl: "\n\nREPLY LANGUAGE: respond in Dutch. Use the informal je/jij register throughout (never formal u); use natural Dutch health vocabulary.",
|
||||
ko: "\n\nREPLY LANGUAGE: respond in Korean. Use the 해요체 register throughout (never 합쇼체 or plain 한다체); use natural Korean health vocabulary.",
|
||||
ru: "\n\nREPLY LANGUAGE — THIS OVERRIDES THE LANGUAGE RULE ABOVE. Any \"Respond in English\" instruction earlier in this prompt does NOT apply: respond in Russian (русский язык), address the user with «вы», use natural Russian health vocabulary and the standard Russian lab terminology. Never answer in English.",
|
||||
"pt-BR":
|
||||
"\n\nREPLY LANGUAGE: respond in Brazilian Portuguese (pt-BR, never European Portuguese). Address the user as você throughout; use natural Brazilian health vocabulary.",
|
||||
};
|
||||
@@ -1297,8 +1298,13 @@ export function getCoachSystemPrompt(
|
||||
base = COACH_PROMPT_DE;
|
||||
} else if (locale === "en") {
|
||||
base = COACH_PROMPT_EN;
|
||||
} else if (locale === "ko" || locale === "nl" || locale === "pt-BR") {
|
||||
// ko, nl and pt-BR ship no safety-contract matrix, so they are not a
|
||||
} else if (
|
||||
locale === "ko" ||
|
||||
locale === "nl" ||
|
||||
locale === "pt-BR" ||
|
||||
locale === "ru"
|
||||
) {
|
||||
// ko, nl, pt-BR and ru ship no safety-contract matrix, so they are not a
|
||||
// NativeCoachLocale.
|
||||
base = COACH_PROMPT_EN + LOCALE_REPLY_FOOTER_FALLBACK[locale];
|
||||
} else {
|
||||
|
||||
@@ -994,7 +994,12 @@ export function getStrictInsightsSystemPrompt(locale: Locale): string {
|
||||
if (locale === "en") return SYSTEM_PROMPT_EN;
|
||||
// ko, nl and pt-BR ship no safety-contract matrix, so they are not a
|
||||
// NativeCoachLocale.
|
||||
if (locale === "ko" || locale === "nl" || locale === "pt-BR") {
|
||||
if (
|
||||
locale === "ko" ||
|
||||
locale === "nl" ||
|
||||
locale === "pt-BR" ||
|
||||
locale === "ru"
|
||||
) {
|
||||
return SYSTEM_PROMPT_EN + INSIGHTS_LOCALE_REPLY_FOOTER_FALLBACK[locale];
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -16,6 +16,7 @@ export const INSIGHTS_LOCALE_REPLY_FOOTER_FALLBACK: Record<
|
||||
pl: "\n\nREPLY LANGUAGE: render all user-facing strings in Polish. Use natural Polish health vocabulary with formal Pan/Pani register for medical-adjacent topics. The severity / sourceWindow / sourceMetric / topic enum values stay in lowercase English exactly as listed in OUTPUT FORMAT — those are contract keys, NOT translations.",
|
||||
nl: "\n\nREPLY LANGUAGE: render all user-facing strings in Dutch, in the informal je/jij register throughout (never formal u). Use natural Dutch health vocabulary. The severity / sourceWindow / sourceMetric / topic enum values stay in lowercase English exactly as listed in OUTPUT FORMAT — those are contract keys, NOT translations.",
|
||||
ko: "\n\nREPLY LANGUAGE: render all user-facing strings in Korean, in the 해요체 register throughout (never 합쇼체 or plain 한다체). Use natural Korean health vocabulary. The severity / sourceWindow / sourceMetric / topic enum values stay in lowercase English exactly as listed in OUTPUT FORMAT — those are contract keys, NOT translations.",
|
||||
ru: "\n\nREPLY LANGUAGE — THIS OVERRIDES THE LANGUAGE RULE ABOVE. The \"Respond in English\" instruction earlier in this prompt does NOT apply to this request: write EVERY user-facing string in Russian (русский язык), with natural Russian medical vocabulary and the standard Russian lab terminology, addressing the user with «вы». Never answer in English. The severity / sourceWindow / sourceMetric / topic enum values still stay in lowercase English exactly as listed in OUTPUT FORMAT — those are contract keys, NOT translations.",
|
||||
"pt-BR":
|
||||
"\n\nREPLY LANGUAGE: render all user-facing strings in Brazilian Portuguese (pt-BR, never European Portuguese), addressing the user as você throughout. Use natural Brazilian health vocabulary. The severity / sourceWindow / sourceMetric / topic enum values stay in lowercase English exactly as listed in OUTPUT FORMAT — those are contract keys, NOT translations.",
|
||||
};
|
||||
@@ -51,6 +51,7 @@ import { bank as pl } from "./outbound-screen/locales/pl";
|
||||
import { bank as nl } from "./outbound-screen/locales/nl";
|
||||
import { bank as ko } from "./outbound-screen/locales/ko";
|
||||
import { bank as ptBR } from "./outbound-screen/locales/pt-br";
|
||||
import { bank as ru } from "./outbound-screen/locales/ru";
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
INN_STEM,
|
||||
@@ -85,6 +86,7 @@ const LOCALE_BANKS: Readonly<Record<Locale, LocaleBank>> = {
|
||||
nl,
|
||||
ko,
|
||||
"pt-BR": ptBR,
|
||||
ru,
|
||||
};
|
||||
|
||||
/** One value per locale, read off its bank. */
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/** Outbound-screen banks for Russian, composed by `../../outbound-screen.ts`. */
|
||||
import {
|
||||
DOSE_UNIT,
|
||||
DRUG_UNIT,
|
||||
UNIT_END,
|
||||
ENGINE,
|
||||
medicationExtras,
|
||||
unlessDeferred,
|
||||
type LocaleBank,
|
||||
} from "../shared";
|
||||
|
||||
const RU_MED_NOUN =
|
||||
"(?:доз[аыуей]|дозу|таблетк\\w*|капсул\\w*|препарат\\w*|лекарств\\w*|инсулин\\w*|инъекц\\w*|укол\\w*|средств\\w*|терапи\\w*|при[её]м\\w*)";
|
||||
|
||||
/** Russian dosage-form counters, with the case endings an object takes. */
|
||||
const RU_COUNT = "(?:таблетк\\w*|капсул\\w*|капл\\w*|доз\\w*|единиц\\w*|ед\\.?|мл)";
|
||||
|
||||
const RU_TARGET = `(?:[\\d.,]+(?:\\s*${DRUG_UNIT}${UNIT_END}|\\s*${RU_COUNT})|(?:одну|одна|две|два|три|четыре|половину|полторы|пол)\\s*${RU_COUNT})`;
|
||||
|
||||
/**
|
||||
* A Russian imperative or recommendation that closes a dose clause. The
|
||||
* prohibitive forms ("не увеличивайте", "не снижайте") never reach the verb
|
||||
* from a negation-aware lead, and the talking-about forms ("увеличил") carry a
|
||||
* different ending.
|
||||
*/
|
||||
const RU_IMPERATIVE =
|
||||
"(?:(?:увеличьте|увеличь|повысьте|повысь|поднимите|подними|нарастите|снизьте|снизь|уменьшите|уменьши|понизьте|понизь|опустите|опусти|сократите|сократи|удвойте|удвой|уполовиньте|разделите|пропустите|пропусти|отмените|отмени|прекратите|прекрати|примите|принимайте|начните|начинайте|перейдите))";
|
||||
|
||||
// Russian writes "12%" with digits; the spelled-out counters ("двенадцать
|
||||
// процентов") are rare in model prose and collide with the numeral declension,
|
||||
// so only the digit forms are matched.
|
||||
const QUAL_NUM_RU = "(?:\\d{1,3}\\s*(?:%|процент\\w*))";
|
||||
|
||||
const RISK_NOUN_RU = "(?:риск\\w*|вероятност\\w*|шанс\\w*)";
|
||||
|
||||
const HORIZON_RU =
|
||||
"(?:(?:10|десяти)[- ]?летн\\w*|пожизненн\\w*)\\s*(?:риск\\w*|сердечно[- ]сосудист\\w*|смертност\\w*)";
|
||||
|
||||
const RESULT_VERB_RU =
|
||||
"(?:относит\\w*|попада\\w*|классифицир\\w*|перевод\\w*|помеща\\w*|оказыва\\w*\\s*в)";
|
||||
|
||||
const RISK_BAND_RU =
|
||||
"(?:высок|повышенн|промежуточн|умеренн|низк)\\w*\\s*(?:риск\\w*|категор\\w*|групп\\w*|диапазон\\w*|полос\\w*)";
|
||||
|
||||
const RISK_LEVEL_RU = "(?:высок|повышен|умеренн|низк|пограничн|тревожн)";
|
||||
|
||||
const RISK_VERDICT_RU = `(?:[—–-]|это|является|находится|означает)\\s*[^.?!]{0,10}${RISK_LEVEL_RU}`;
|
||||
|
||||
const RU_MED_CLASS =
|
||||
"(?:антикоагулянт\\w*|антиагрегант\\w*|статины|статин\\w*|бета[- ]блокатор\\w*|диуретик\\w*|антидепрессант\\w*|метформин\\w*|инсулин\\w*)";
|
||||
|
||||
/** Signals that the number-free change shapes stand down for (`unlessDeferred`). */
|
||||
const RU_DEFERRAL = "\\?|врач\\w*|доктор\\w*|специалист\\w*|эндокринолог\\w*|фармацевт\\w*|обсуди\\w*|посовету\\w*|спроси\\w*|если\\s+только|не\\s+";
|
||||
|
||||
export const bank: LocaleBank = {
|
||||
vocabulary: (names) =>
|
||||
`(?:${RU_MED_NOUN}|(?:${medicationExtras(RU_MED_CLASS, names, false)})\\S{0,3})`,
|
||||
doseChangeClass: (med) => [
|
||||
// A stated amount closed by an imperative or a recommendation:
|
||||
// "2000 мг увеличьте", "принимайте 2 таблетки".
|
||||
new RegExp(`${RU_TARGET}[^.?!]{0,25}${RU_IMPERATIVE}`, "i"),
|
||||
// A medication object halved, doubled, skipped or stopped by imperative.
|
||||
new RegExp(
|
||||
`${med}[^.?!]{0,20}(?:(?:вдвое|в\\s*два\\s*раза|пополам|вдвое\\s+меньше|наполовину)\\s*(?:увеличьте|увеличь|снизьте|снизь|уменьшите|уменьши|сократите|разделите|удвойте)|(?:пропустите|пропусти|пропускать|отмените|отмени|прекратите|прекрати|бросьте))`,
|
||||
),
|
||||
new RegExp(
|
||||
`(?:доз[уа]|при[её]м|инъекци\\w*|укол\\w*)\\s*(?:пропустите|пропусти|пропускать|отмените|отмени|прекратите|прекрати)`,
|
||||
),
|
||||
// One more dose of a medication, closed by an imperative:
|
||||
// "примите ещё одну таблетку метформина".
|
||||
new RegExp(
|
||||
`${med}[^.?!]{0,12}(?:ещ[её]|дополнительн\\w*|лишн\\w*)\\s*(?:одну|одна|две|дозу|таблетку|капсулу)?\\s*(?:примите|принимайте|выпейте|прими)`,
|
||||
),
|
||||
// A medication object raised or lowered by imperative with no amount,
|
||||
// standing down where a question, a negation or a clinician appears.
|
||||
unlessDeferred(
|
||||
RU_DEFERRAL,
|
||||
new RegExp(
|
||||
`${med}[^.?!\\d]{0,8}?(?:увеличьте|увеличь|повысьте|повысь|поднимите|подними|снизьте|снизь|уменьшите|уменьши|понизьте|понизь|опустите|опусти|сократите|сократи)`,
|
||||
),
|
||||
),
|
||||
],
|
||||
// A target dose: the unit sits on the number and a change verb or a
|
||||
// recommending lead-in frames it. The permitted restatement ("вы на 7,5 мг
|
||||
// на этой неделе") names no change verb and does not trip.
|
||||
dose: [
|
||||
new RegExp(
|
||||
`[\\d.,]+\\s*${DOSE_UNIT}\\s*(?:до|вплоть\\s*до)[^.?!]{0,20}(?:увелич|повыс|подним|нараст|начинай|переход|сниз|уменьш|пониз|опуст|сократ)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:увелич|повыс|подним|нараст|сниз|уменьш|пониз|опуст|сократ)\\w*[^.?!]{0,40}[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:рекоменд|совету|предлаг|стоит|нужно|следует|можно)[^.?!]{0,40}[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}[^.?!]{0,40}(?:рекоменд|совету|предлаг|стоит|принимайте|примите|начните|перейдите)`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:следующ|нов|целев|рабоч)\\w*\\s*(?:доз|этап|титрац)\\w*[^.?!]{0,30}[\\d.,]+\\s*${DOSE_UNIT}${UNIT_END}`,
|
||||
"i",
|
||||
),
|
||||
unlessDeferred(
|
||||
RU_DEFERRAL,
|
||||
/(?:доз|таблетк|капсул|инсулин|препарат|лекарств|приём|прием)[^.?!]{0,25}(?:вдвое|в два раза|пополам|пропуст|отмен|прекрат|удво|уполовин)[^.?!]{0,40}(?:недел|дн|месяц|попробуйте|можно)/,
|
||||
),
|
||||
],
|
||||
risk: [
|
||||
new RegExp(`${RISK_NOUN_RU}[^.?!]{0,15}${QUAL_NUM_RU}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_RU}[^.?!]{0,15}${RISK_NOUN_RU}`, "i"),
|
||||
new RegExp(`${HORIZON_RU}[^.?!]{0,40}${QUAL_NUM_RU}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_RU}[^.?!]{0,40}${HORIZON_RU}`, "i"),
|
||||
new RegExp(`\\b${ENGINE}\\b[^.?!]{0,50}${QUAL_NUM_RU}`, "i"),
|
||||
new RegExp(`${QUAL_NUM_RU}[^.?!]{0,50}\\b${ENGINE}\\b`, "i"),
|
||||
new RegExp(
|
||||
`(?:\\b${ENGINE}\\b|${HORIZON_RU})[^.?!]{0,60}${RESULT_VERB_RU}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:\\b${ENGINE}\\b|${HORIZON_RU})[^.?!]{0,60}${RISK_BAND_RU}`,
|
||||
"i",
|
||||
),
|
||||
new RegExp(
|
||||
`(?:\\b${ENGINE}\\b|${HORIZON_RU})[^.?!]{0,40}${RISK_VERDICT_RU}`,
|
||||
"i",
|
||||
),
|
||||
],
|
||||
causal: [
|
||||
/потому\s+что/i,
|
||||
/из[- ]за/i,
|
||||
/причин\w*/i,
|
||||
/вызван\w*/i,
|
||||
/приводит\s+к/i,
|
||||
/привел[оаи]\s+к/i,
|
||||
/вследствие/i,
|
||||
/обусловлен\w*/i,
|
||||
/следстви\w*/i,
|
||||
/благодаря/i,
|
||||
/явля\w+\s+(?:причиной|результатом)/i,
|
||||
],
|
||||
continuation:
|
||||
/(?:продолжайте|продолжайте\s+принимать|продолжить\s+при[её]м|как\s+назначено|по\s+назначению|как\s+обычно|как\s+и\s+раньше|в\s+той\s+же\s+дозе|не\s+меняйте)/i,
|
||||
changeStem:
|
||||
/(?:увелич|повыс|подним|нараст|сниз|уменьш|пониз|опуст|сократ|удво|уполовин|пропуст|отмен|прекрат|перейд|начнит|измен|титр)/i,
|
||||
};
|
||||
@@ -33,6 +33,7 @@ const INTL_LOCALE_MAP: Record<Locale, string> = {
|
||||
ko: "ko-KR",
|
||||
nl: "nl-NL",
|
||||
"pt-BR": "pt-BR",
|
||||
ru: "ru-RU",
|
||||
};
|
||||
|
||||
export function resolveIntlLocale(locale: Locale): string {
|
||||
|
||||
@@ -8,6 +8,7 @@ export const locales = [
|
||||
"ko",
|
||||
"nl",
|
||||
"pt-BR",
|
||||
"ru",
|
||||
] as const;
|
||||
export type Locale = (typeof locales)[number];
|
||||
|
||||
@@ -68,6 +69,7 @@ export const localeLanguageNames: Record<Locale, string> = {
|
||||
ko: "Korean",
|
||||
nl: "Dutch",
|
||||
"pt-BR": "Brazilian Portuguese",
|
||||
ru: "Russian",
|
||||
};
|
||||
|
||||
export const localeLabels: Record<Locale, string> = {
|
||||
@@ -80,6 +82,7 @@ export const localeLabels: Record<Locale, string> = {
|
||||
ko: "한국어",
|
||||
nl: "Nederlands",
|
||||
"pt-BR": "Português (Brasil)",
|
||||
ru: "Русский",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,7 @@ const loaders: Record<Locale, () => Promise<{ default: MessageBundle }>> = {
|
||||
ko: () => import("../../../messages/ko.json"),
|
||||
nl: () => import("../../../messages/nl.json"),
|
||||
"pt-BR": () => import("../../../messages/pt-BR.json"),
|
||||
ru: () => import("../../../messages/ru.json"),
|
||||
};
|
||||
|
||||
function initialCache(): Partial<Record<Locale, MessageBundle>> {
|
||||
|
||||
@@ -323,6 +323,45 @@ export const RANGE_PROSE: Record<Locale, RangeProse> = {
|
||||
lower: ["최소", "최저"],
|
||||
filler: ["참고치", "참고범위", "정상치", "정상범위", "기준치", "기준범위"],
|
||||
},
|
||||
// Russian lab sheets print "3,5 - 5,0", "от 3,5 до 5,0", "до 5,6",
|
||||
// "не более 5,6", "не менее 3,5" — "от"/"до" are both a bound prefix and a
|
||||
// filler, which is why they appear in all three lists.
|
||||
ru: {
|
||||
twoSided: ["до", "по"],
|
||||
upper: [
|
||||
"до",
|
||||
"не более",
|
||||
"не выше",
|
||||
"менее",
|
||||
"меньше",
|
||||
"ниже",
|
||||
"макс.",
|
||||
"макс",
|
||||
"максимум",
|
||||
],
|
||||
lower: [
|
||||
"от",
|
||||
"не менее",
|
||||
"не ниже",
|
||||
"более",
|
||||
"больше",
|
||||
"выше",
|
||||
"мин.",
|
||||
"мин",
|
||||
"минимум",
|
||||
],
|
||||
filler: [
|
||||
"от",
|
||||
"до",
|
||||
"референс",
|
||||
"референсные значения",
|
||||
"референсный интервал",
|
||||
"норма",
|
||||
"показатели нормы",
|
||||
"реф.",
|
||||
"реф",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/** Escape a literal for regex use and let any run of whitespace join its words. */
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* GAD-7, Russian: a working translation written for HealthLog (not a
|
||||
* published validated one), faithful to the English original and on the same
|
||||
* 0-3 scale.
|
||||
*
|
||||
* Источник: Рабочий перевод HealthLog с английского оригинала GAD-7 (Spitzer, Kroenke, Williams & Löwe, Arch Intern Med 2006;166:1092-1097). Валидированный русскоязычный вариант в открытом доступе отсутствует.
|
||||
*/
|
||||
import type { InstrumentWording } from "../types";
|
||||
|
||||
const wording: InstrumentWording = {
|
||||
validated: false,
|
||||
stems: {
|
||||
period:
|
||||
"За последние 2 недели как часто вас беспокоили следующие проблемы?",
|
||||
},
|
||||
items: [
|
||||
"Вы чувствовали нервозность, тревогу или напряжённость",
|
||||
"Вам не удавалось остановить или сдержать беспокойство",
|
||||
"Вы слишком много беспокоились о разных вещах",
|
||||
"Вам было трудно расслабиться",
|
||||
"Вы были настолько беспокойны, что трудно было усидеть на месте",
|
||||
"Вы легко раздражались",
|
||||
"Вы боялись, что может случиться что-то ужасное",
|
||||
],
|
||||
anchors: {
|
||||
all: {
|
||||
0: "Совсем нет",
|
||||
1: "Несколько дней",
|
||||
2: "Больше половины дней",
|
||||
3: "Почти каждый день",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default wording;
|
||||
@@ -35,6 +35,7 @@ import phq9Pl from "./phq9/pl";
|
||||
import phq9Ko from "./phq9/ko";
|
||||
import phq9Nl from "./phq9/nl";
|
||||
import phq9PtBr from "./phq9/pt-br";
|
||||
import phq9Ru from "./phq9/ru";
|
||||
import gad7En from "./gad7/en";
|
||||
import gad7De from "./gad7/de";
|
||||
import gad7Es from "./gad7/es";
|
||||
@@ -44,6 +45,7 @@ import gad7Pl from "./gad7/pl";
|
||||
import gad7Ko from "./gad7/ko";
|
||||
import gad7Nl from "./gad7/nl";
|
||||
import gad7PtBr from "./gad7/pt-br";
|
||||
import gad7Ru from "./gad7/ru";
|
||||
import who5En from "./who5/en";
|
||||
import who5De from "./who5/de";
|
||||
import who5Es from "./who5/es";
|
||||
@@ -53,6 +55,7 @@ import who5Pl from "./who5/pl";
|
||||
import who5Ko from "./who5/ko";
|
||||
import who5Nl from "./who5/nl";
|
||||
import who5PtBr from "./who5/pt-br";
|
||||
import who5Ru from "./who5/ru";
|
||||
import sciEn from "./sci/en";
|
||||
import sciDe from "./sci/de";
|
||||
import sciEs from "./sci/es";
|
||||
@@ -62,6 +65,7 @@ import sciPl from "./sci/pl";
|
||||
import sciKo from "./sci/ko";
|
||||
import sciNl from "./sci/nl";
|
||||
import sciPtBr from "./sci/pt-br";
|
||||
import sciRu from "./sci/ru";
|
||||
|
||||
export const WORDING: Readonly<
|
||||
Record<InstrumentId, Readonly<Record<Locale, InstrumentWording>>>
|
||||
@@ -76,6 +80,7 @@ export const WORDING: Readonly<
|
||||
ko: phq9Ko,
|
||||
nl: phq9Nl,
|
||||
"pt-BR": phq9PtBr,
|
||||
ru: phq9Ru,
|
||||
},
|
||||
GAD7: {
|
||||
en: gad7En,
|
||||
@@ -87,6 +92,7 @@ export const WORDING: Readonly<
|
||||
ko: gad7Ko,
|
||||
nl: gad7Nl,
|
||||
"pt-BR": gad7PtBr,
|
||||
ru: gad7Ru,
|
||||
},
|
||||
WHO5: {
|
||||
en: who5En,
|
||||
@@ -98,6 +104,7 @@ export const WORDING: Readonly<
|
||||
ko: who5Ko,
|
||||
nl: who5Nl,
|
||||
"pt-BR": who5PtBr,
|
||||
ru: who5Ru,
|
||||
},
|
||||
SCI: {
|
||||
en: sciEn,
|
||||
@@ -109,5 +116,6 @@ export const WORDING: Readonly<
|
||||
ko: sciKo,
|
||||
nl: sciNl,
|
||||
"pt-BR": sciPtBr,
|
||||
ru: sciRu,
|
||||
},
|
||||
};
|
||||
@@ -18,6 +18,7 @@ const LOADERS: Record<Locale, () => Promise<{ default: LocaleWording }>> = {
|
||||
ko: () => import("./locales/ko"),
|
||||
nl: () => import("./locales/nl"),
|
||||
"pt-BR": () => import("./locales/pt-br"),
|
||||
ru: () => import("./locales/ru"),
|
||||
};
|
||||
|
||||
export async function loadLocaleWording(
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import gad7 from "../gad7/ru";
|
||||
import phq9 from "../phq9/ru";
|
||||
import sci from "../sci/ru";
|
||||
import who5 from "../who5/ru";
|
||||
import type { LocaleWording } from "../types";
|
||||
|
||||
/** Every questionnaire in Russian, loaded as one chunk. */
|
||||
const wording: LocaleWording = { PHQ9: phq9, GAD7: gad7, WHO5: who5, SCI: sci };
|
||||
|
||||
export default wording;
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* PHQ-9, Russian: a working translation written for HealthLog (not a
|
||||
* published validated one), faithful to the English original and on the same
|
||||
* 0-3 scale, so scores stay comparable across languages.
|
||||
*
|
||||
* Источник: Рабочий перевод HealthLog с английского оригинала PHQ-9 (Kroenke, Spitzer & Williams, J Gen Intern Med 2001;16:606-613). Валидированный русскоязычный вариант в открытом доступе отсутствует.
|
||||
*/
|
||||
import type { InstrumentWording } from "../types";
|
||||
|
||||
const wording: InstrumentWording = {
|
||||
validated: false,
|
||||
stems: {
|
||||
period:
|
||||
"За последние 2 недели как часто вас беспокоили следующие проблемы?",
|
||||
},
|
||||
items: [
|
||||
"Вам было неинтересно или не приносило удовольствия то, что вы делали",
|
||||
"Вы чувствовали подавленность, угнетённость или безысходность",
|
||||
"Вам было трудно заснуть, вы плохо спали или спали слишком много",
|
||||
"Вы чувствовали усталость или упадок сил",
|
||||
"У вас был плохой аппетит или вы переедали",
|
||||
"Вы плохо думали о себе, считали себя неудачником или подводили себя и свою семью",
|
||||
"Вам было трудно сосредоточиться, например читать или смотреть телевизор",
|
||||
"Вы двигались или говорили заметно медленнее обычного — или, наоборот, были настолько суетливы и беспокойны, что двигались больше обычного",
|
||||
"Вам приходили мысли, что вам лучше умереть, или о том, чтобы причинить себе вред",
|
||||
],
|
||||
anchors: {
|
||||
all: {
|
||||
0: "Совсем нет",
|
||||
1: "Несколько дней",
|
||||
2: "Больше половины дней",
|
||||
3: "Почти каждый день",
|
||||
},
|
||||
},
|
||||
functional: {
|
||||
title:
|
||||
"Если вы отметили какие-либо проблемы, насколько они затрудняли вам работу, домашние дела или общение с другими людьми?",
|
||||
anchors: {
|
||||
0: "Совсем не затрудняли",
|
||||
1: "Несколько затрудняли",
|
||||
2: "Сильно затрудняли",
|
||||
3: "Очень сильно затрудняли",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default wording;
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* SCI, Russian: a working translation written for HealthLog (not a published
|
||||
* validated one), faithful to the eight items and their anchor sets.
|
||||
*
|
||||
* Источник: Рабочий перевод HealthLog с английского оригинала SCI (Espie CA, Kyle SD, Hames P, et al. The Sleep Condition Indicator. BMJ Open 2014;4:e004183).
|
||||
*/
|
||||
import type { InstrumentWording } from "../types";
|
||||
|
||||
const wording: InstrumentWording = {
|
||||
validated: false,
|
||||
stems: {
|
||||
night: "Думая об обычной ночи за последний месяц…",
|
||||
impact: "Думая о прошедшем месяце, в какой степени плохой сон…",
|
||||
finally: "И последнее…",
|
||||
},
|
||||
items: [
|
||||
"… сколько времени вам нужно, чтобы заснуть?",
|
||||
"… если вы просыпаетесь ночью, сколько времени вы бодрствуете в сумме? (сложите все пробуждения)",
|
||||
"… сколько ночей в неделю у вас есть проблема со сном?",
|
||||
"… как бы вы оценили качество своего сна?",
|
||||
"… влиял на ваше настроение, энергию или отношения?",
|
||||
"… влиял на вашу концентрацию, продуктивность или способность бодрствовать?",
|
||||
"… беспокоил вас в целом?",
|
||||
"… как давно у вас есть проблема со сном?",
|
||||
],
|
||||
anchors: {
|
||||
duration: {
|
||||
0: "≥ 61 мин",
|
||||
1: "46–60 мин",
|
||||
2: "31–45 мин",
|
||||
3: "16–30 мин",
|
||||
4: "0–15 мин",
|
||||
},
|
||||
nights: { 0: "5–7", 1: "4", 2: "3", 3: "2", 4: "0–1" },
|
||||
quality: {
|
||||
0: "Очень плохое",
|
||||
1: "Плохое",
|
||||
2: "Среднее",
|
||||
3: "Хорошее",
|
||||
4: "Очень хорошее",
|
||||
},
|
||||
impact: {
|
||||
0: "Очень сильно",
|
||||
1: "Сильно",
|
||||
2: "Умеренно",
|
||||
3: "Немного",
|
||||
4: "Совсем нет",
|
||||
},
|
||||
problemDuration: {
|
||||
0: "> 1 года",
|
||||
1: "7–12 месяцев",
|
||||
2: "3–6 месяцев",
|
||||
3: "1–2 месяца",
|
||||
4: "Проблемы нет / < 1 месяца",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default wording;
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* WHO-5, Russian: a working translation written for HealthLog (the WHO's own
|
||||
* Russian version is under CC BY-NC-SA and not reproduced verbatim here),
|
||||
* faithful to the five items and the 0-5 scale.
|
||||
*
|
||||
* Источник: Рабочий перевод HealthLog опросника ВОЗ-5 (World Health Organization. The World Health Organization-Five Well-Being Index (WHO-5). Geneva: WHO; 2024, WHO/UCN/MSD/MHE/2024.1).
|
||||
*/
|
||||
import type { InstrumentWording } from "../types";
|
||||
|
||||
const wording: InstrumentWording = {
|
||||
validated: false,
|
||||
stems: { period: "За последние две недели…" },
|
||||
items: [
|
||||
"Я чувствовал себя бодрым и в хорошем настроении",
|
||||
"Я чувствовал себя спокойно и расслабленно",
|
||||
"Я чувствовал себя активным и полным сил",
|
||||
"Я просыпался свежим и отдохнувшим",
|
||||
"Моя повседневная жизнь была наполнена тем, что мне интересно",
|
||||
],
|
||||
anchors: {
|
||||
all: {
|
||||
0: "Ни разу",
|
||||
1: "Иногда",
|
||||
2: "Меньше половины времени",
|
||||
3: "Больше половины времени",
|
||||
4: "Большую часть времени",
|
||||
5: "Всё время",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default wording;
|
||||
@@ -34,6 +34,10 @@ const BARE_WORDS: Readonly<Record<Locale, BareWords>> = {
|
||||
ko: { help: ["도움말", "도움"], greeting: ["안녕", "안녕하세요"] },
|
||||
nl: { help: ["hulp"], greeting: ["hoi", "hallo", "goedemorgen"] },
|
||||
"pt-BR": { help: ["ajuda"], greeting: ["oi", "ola", "bom dia"] },
|
||||
ru: {
|
||||
help: ["помощь", "помоги", "помогите"],
|
||||
greeting: ["привет", "здравствуй", "здравствуйте", "добрый день"],
|
||||
},
|
||||
};
|
||||
|
||||
/** "Help", in every language the app ships. */
|
||||
|
||||
Reference in new issue
Block a user