Skip to content

Commit 2d845ef

Browse files
♻️ refactor(i18n): replace kebab-case with snake_case in FTL keys (#72)
* ♻️ refactor(i18n): replace kebab-case with snake_case in FTL keys - Rename all locale keys from kebab-case to snake_case across en/ and ru/ - Add separator="_" to TranslatorHub so typed method calls match FTL keys - Introduce types.py with TYPE_CHECKING guard for typed TranslatorRunner - Migrate routers to use typed i18n method calls where applicable - Unify TranslatorRunner imports to src.infrastructure.i18n - Remove obsolete stubs.pyi (replaced by types.py) - Update generate_i18n_stubs.py to target types.py - Add generate-i18n justfile command Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ♻️ refactor(bot): refactor admin stats router with logging and typed i18n - Add logger.info calls to all three admin stats handlers - Switch from TranslatorHub to i18n middleware injection - Use typed i18n method calls instead of i18n.get() strings - Extract stats keyboard markup to utils/markups/admin.py - Add check_alive_btn locale key for en/ and ru/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ♻️ refactor(bot): use typed i18n methods in commands router Replace i18n.get() string calls with typed method calls in _start_onboarding and command_start_handler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ♻️ refactor(bot): use typed i18n methods in referral router - Switch from TranslatorHub to i18n middleware injection - Replace i18n.get() string calls with typed method calls - Remove duplicate referral_info call Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ♻️ refactor(bot): use typed i18n methods in settings markups Replace i18n.get() string calls with typed method calls for btn_settings, btn_language, and btn_back buttons. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * remove duplicated code --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2b3e09c commit 2d845ef

23 files changed

Lines changed: 200 additions & 178 deletions

File tree

justfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,8 @@ test-db-up:
6464

6565
lint:
6666
uv run ruff format src tests
67-
uv run ruff check src tests --fix
67+
uv run ruff check src tests --fix
68+
69+
generate-i18n:
70+
uv run python scripts/generate_i18n_stubs.py
71+
uv run ruff format src/infrastructure/i18n/types.py

locales/en/admin.ftl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Admin-only messages
2-
example-executed = Example admin command executed
3-
bot-started = Bot has started!
2+
example_executed = Example admin command executed
3+
bot_started = Bot has started!

locales/en/common.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# Common strings shared across features
2-
echo-unknown-message = I don't understand this message. Use /start to begin.
2+
echo_unknown_message = I don't understand this message. Use /start to begin.

locales/en/settings.ftl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
# Settings messages
2-
settings-title = ⚙️ Settings
2+
settings_title = ⚙️ Settings
33
44
Choose an option:
5-
settings-language-title = 🌐 Language
5+
settings_language_title = 🌐 Language
66
77
Select your preferred language:
8-
settings-language-changed = ✅ Language changed successfully.
8+
settings_language_changed = ✅ Language changed successfully.
99
1010
# Onboarding
11-
onboarding-language = 🌐 Please select your language:
11+
onboarding_language = 🌐 Please select your language:
1212
1313
# Buttons
14-
btn-language = 🌐 Language
15-
btn-settings = ⚙️ Settings
16-
btn-back = ↩️ Back
17-
lang-en = 🇬🇧 English
18-
lang-ru = 🇷🇺 Русский
14+
btn_language = 🌐 Language
15+
btn_settings = ⚙️ Settings
16+
btn_back = ↩️ Back
17+
lang_en = 🇬🇧 English
18+
lang_ru = 🇷🇺 Русский

locales/en/stats.ftl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
stats-overview =
1+
stats_overview =
22
📊 Statistics
33
44
Total users: { $total }
55
Referred: { $referred } ({ $referred_pct }%)
66
Organic: { $organic } ({ $organic_pct }%)
77
8-
stats-top-inviters-btn = 🏆 Top inviters
8+
stats_top_inviters_btn = 🏆 Top inviters
9+
check_alive_btn = 🫀 Check alive
910
10-
stats-top-inviters-header = 🏆 Top { $limit } inviters:
11+
stats_top_inviters_header = 🏆 Top { $limit } inviters:
1112
12-
stats-no-inviters = No inviters yet
13+
stats_no_inviters = No inviters yet
1314
14-
referral-info =
15+
referral_info =
1516
🔗 Your referral link:
1617
{ $link }
1718
1819
👥 Invited users: { $count }
1920
20-
referral-user-not-found = User not found. Please use /start first.
21+
referral_user_not_found = User not found. Please use /start first.

locales/ru/admin.ftl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Сообщения для администраторов
2-
example-executed = Пример админской команды выполнен
3-
bot-started = Бот запущен!
2+
example_executed = Пример админской команды выполнен
3+
bot_started = Бот запущен!

locales/ru/common.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# Общие строки
2-
echo-unknown-message = Я не понимаю это сообщение. Используйте /start чтобы начать.
2+
echo_unknown_message = Я не понимаю это сообщение. Используйте /start чтобы начать.

locales/ru/settings.ftl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
# Сообщения настроек
2-
settings-title = ⚙️ Настройки
2+
settings_title = ⚙️ Настройки
33
44
Выберите опцию:
5-
settings-language-title = 🌐 Язык
5+
settings_language_title = 🌐 Язык
66
77
Выберите язык:
8-
settings-language-changed = ✅ Язык успешно изменён.
8+
settings_language_changed = ✅ Язык успешно изменён.
99
1010
# Онбординг
11-
onboarding-language = 🌐 Пожалуйста, выберите язык:
11+
onboarding_language = 🌐 Пожалуйста, выберите язык:
1212
1313
# Кнопки
14-
btn-language = 🌐 Язык
15-
btn-settings = ⚙️ Настройки
16-
btn-back = ↩️ Назад
17-
lang-en = 🇬🇧 English
18-
lang-ru = 🇷🇺 Русский
14+
btn_language = 🌐 Язык
15+
btn_settings = ⚙️ Настройки
16+
btn_back = ↩️ Назад
17+
lang_en = 🇬🇧 English
18+
lang_ru = 🇷🇺 Русский

locales/ru/stats.ftl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
stats-overview =
1+
stats_overview =
22
📊 Статистика
33
44
Всего пользователей: { $total }
55
По рефералам: { $referred } ({ $referred_pct }%)
66
Органика: { $organic } ({ $organic_pct }%)
77
8-
stats-top-inviters-btn = 🏆 Топ инвайтеров
8+
stats_top_inviters_btn = 🏆 Топ инвайтеров
9+
check_alive_btn = 🫀 Живые юзеры
910
10-
stats-top-inviters-header = 🏆 Топ-{ $limit } инвайтеров:
11+
stats_top_inviters_header = 🏆 Топ-{ $limit } инвайтеров:
1112
12-
stats-no-inviters = Пока нет инвайтеров
13+
stats_no_inviters = Пока нет инвайтеров
1314
14-
referral-info =
15+
referral_info =
1516
🔗 Ваша реферальная ссылка:
1617
{ $link }
1718
1819
👥 Приглашённых пользователей: { $count }
1920
20-
referral-user-not-found = Пользователь не найден. Сначала используйте /start.
21+
referral_user_not_found = Пользователь не найден. Сначала используйте /start.

scripts/generate_i18n_stubs.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/usr/bin/env python3
2-
"""Generate Python type stubs for fluentogram translations.
2+
"""Generate Python types file for fluentogram translations.
33
4-
This script parses .ftl files and generates a .pyi stub file
5-
that provides IDE autocomplete for translation keys.
4+
This script parses .ftl files and generates a types.py file
5+
that provides IDE autocomplete for translation keys with runtime
6+
compatibility via TYPE_CHECKING guard.
67
"""
78

89
import re
@@ -65,39 +66,53 @@ def extract_ftl_keys(ftl_content: str) -> list[tuple[str, list[str]]]:
6566
return keys
6667

6768

68-
def generate_stub_content(all_keys: dict[str, list[str]]) -> str:
69-
"""Generate .pyi stub content for FluentTranslator."""
69+
def generate_types_content(all_keys: dict[str, list[str]]) -> str:
70+
"""Generate types.py content with TYPE_CHECKING guard for FluentTranslator."""
7071
lines = [
71-
'"""Auto-generated type stubs for fluentogram translations.',
72+
'"""Auto-generated types file for fluentogram translations.',
7273
"",
7374
"Generated by scripts/generate_i18n_stubs.py",
74-
"Do not edit manually - regenerate with: python scripts/generate_i18n_stubs.py",
75+
"Do not edit manually - regenerate with: uv run python scripts/generate_i18n_stubs.py",
7576
'"""',
7677
"",
78+
"from typing import TYPE_CHECKING, TypeVar",
7779
"",
78-
"class TranslatorRunner:",
79-
' """Type stubs for FluentTranslator with all available translation keys."""',
80+
"if TYPE_CHECKING:",
81+
' _I18nArg = TypeVar("_I18nArg", str, int)',
82+
"",
83+
" class TranslatorRunner:",
84+
' """Type stubs for FluentTranslator with all available translation keys."""',
85+
"",
86+
" def get(self, key: str, **kwargs: _I18nArg) -> str: ...",
8087
"",
8188
]
8289

8390
for key, params in sorted(all_keys.items()):
8491
# Convert kebab-case to snake_case for method name
8592
method_name = key.replace("-", "_")
8693
if params:
87-
param_str = ", ".join(f"{p}: str | int" for p in params)
88-
lines.append(f" def {method_name}(self, *, {param_str}) -> str: ...")
94+
param_str = ", ".join(f"{p}: _I18nArg" for p in params)
95+
lines.append(f" def {method_name}(self, *, {param_str}) -> str: ...")
8996
else:
90-
lines.append(f" def {method_name}(self) -> str: ...")
97+
lines.append(f" def {method_name}(self) -> str: ...")
98+
99+
lines.extend(
100+
[
101+
"",
102+
"else:",
103+
" from fluentogram import TranslatorRunner # noqa: F401",
104+
"",
105+
]
106+
)
91107

92-
lines.append("")
93108
return "\n".join(lines)
94109

95110

96111
def main() -> None:
97-
"""Generate stubs from locale files."""
112+
"""Generate types file from locale files."""
98113
project_root = Path(__file__).parent.parent
99114
locales_dir = project_root / "locales"
100-
output_file = project_root / "src" / "infrastructure" / "i18n" / "stubs.pyi"
115+
output_file = project_root / "src" / "infrastructure" / "i18n" / "types.py"
101116

102117
# Use English as the reference locale
103118
en_dir = locales_dir / "en"
@@ -110,7 +125,7 @@ def main() -> None:
110125

111126
# Parse all .ftl files in the English locale
112127
for ftl_file in en_dir.glob("*.ftl"):
113-
content = ftl_file.read_text()
128+
content = ftl_file.read_text(encoding="utf-8")
114129
keys = extract_ftl_keys(content)
115130
for key, params in keys:
116131
all_keys[key] = params
@@ -120,12 +135,12 @@ def main() -> None:
120135
print("Error: No translation keys found")
121136
return
122137

123-
# Generate stub content
124-
stub_content = generate_stub_content(all_keys)
138+
# Generate types content
139+
types_content = generate_types_content(all_keys)
125140

126-
# Write stub file
141+
# Write types file
127142
output_file.parent.mkdir(parents=True, exist_ok=True)
128-
output_file.write_text(stub_content)
143+
output_file.write_text(types_content)
129144
print(f"\nGenerated {output_file} with {len(all_keys)} translation methods")
130145

131146

0 commit comments

Comments
 (0)