Translate UI and user-facing messages to Ukrainian (#5)
- All frontend pages, labels, notices and errors; html lang=uk, uk-UA money format - Brand "Assistant System" in the top bar and page title - Backend error details returned to the UI (auth, orders, receipts, cash registers, Checkbox/CRM/NP errors) and CLI output - Tests updated for the new messages Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,7 @@ SessionDep = Annotated[AsyncSession, Depends(get_session)]
|
||||
|
||||
_UNAUTHORIZED = HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Требуется аутентификация",
|
||||
detail="Потрібна автентифікація",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
@@ -76,7 +76,7 @@ def require_roles(*roles: UserRole) -> Callable[..., Coroutine[Any, Any, User]]:
|
||||
if user.role not in allowed:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Недостаточно прав для этого действия",
|
||||
detail="Недостатньо прав для цієї дії",
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ async def change_password(
|
||||
) -> Response:
|
||||
if not verify_password(payload.current_password, user.password_hash):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="Текущий пароль указан неверно"
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail="Поточний пароль вказано неправильно"
|
||||
)
|
||||
|
||||
user.password_hash = hash_password(payload.new_password)
|
||||
|
||||
@@ -44,7 +44,7 @@ async def _ensure_np_key_unique(
|
||||
if same:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=f"Этот ключ Новой Почты уже привязан к кассе «{register.name}»",
|
||||
detail=f"Цей ключ Нової Пошти вже прив'язаний до каси «{register.name}»",
|
||||
)
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ async def update_cash_register(
|
||||
) -> CashRegisterOut:
|
||||
register = await session.get(CashRegister, register_id)
|
||||
if register is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Касса не найдена")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Касу не знайдено")
|
||||
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
if "license_key" in changes:
|
||||
@@ -139,7 +139,7 @@ async def check_cash_register(
|
||||
"""Пробный вход кассира в Checkbox — проверка ключа лицензии и PIN."""
|
||||
register = await session.get(CashRegister, register_id)
|
||||
if register is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Касса не найдена")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Касу не знайдено")
|
||||
try:
|
||||
await checkbox.sign_in(credentials(register))
|
||||
except (CheckboxError, crypto.DecryptionError) as exc:
|
||||
|
||||
@@ -50,7 +50,7 @@ async def update_order(
|
||||
except orders_service.OrderEditError as exc:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc
|
||||
if result is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Заказ не найден")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Замовлення не знайдено")
|
||||
|
||||
order, changed = result
|
||||
if changed:
|
||||
@@ -75,7 +75,7 @@ async def delete_order(
|
||||
) -> None:
|
||||
order = await orders_service.delete_order(session, order_id)
|
||||
if order is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Заказ не найден")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Замовлення не знайдено")
|
||||
|
||||
await audit.record(
|
||||
session,
|
||||
|
||||
@@ -60,7 +60,7 @@ async def list_receipts(session: SessionDep, order_id: str | None = None) -> lis
|
||||
async def get_receipt(receipt_id: uuid.UUID, session: SessionDep) -> ReceiptOut:
|
||||
receipt = await session.get(Receipt, receipt_id)
|
||||
if receipt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Чек не найден")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Чек не знайдено")
|
||||
return ReceiptOut.from_receipt(receipt)
|
||||
|
||||
|
||||
@@ -83,5 +83,5 @@ async def cancel_receipt(
|
||||
status_code=status.HTTP_502_BAD_GATEWAY, detail=f"Checkbox: {exc}"
|
||||
) from exc
|
||||
if receipt is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Чек не найден")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Чек не знайдено")
|
||||
return ReceiptOut.from_receipt(receipt)
|
||||
|
||||
@@ -71,7 +71,7 @@ async def create_user(
|
||||
await session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="Пользователь с таким email уже существует",
|
||||
detail="Користувач із таким email уже існує",
|
||||
) from exc
|
||||
|
||||
return UserOut.model_validate(user)
|
||||
@@ -87,7 +87,7 @@ async def update_user(
|
||||
) -> UserOut:
|
||||
user = await session.get(User, user_id)
|
||||
if user is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Пользователь не найден")
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Користувача не знайдено")
|
||||
|
||||
changes = payload.model_dump(exclude_unset=True)
|
||||
|
||||
@@ -97,12 +97,12 @@ async def update_user(
|
||||
if changes.get("is_active") is False:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Нельзя отключить собственную учётную запись",
|
||||
detail="Не можна вимкнути власний обліковий запис",
|
||||
)
|
||||
if "role" in changes and changes["role"] != UserRole.ADMIN:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Нельзя снять с себя роль администратора",
|
||||
detail="Не можна зняти із себе роль адміністратора",
|
||||
)
|
||||
|
||||
if (new_password := changes.pop("password", None)) is not None:
|
||||
|
||||
Reference in New Issue
Block a user