Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
4d9f1f9
feat(users): add Language enum (#505)
mustafa-5493 May 10, 2026
1848c34
feat(users): add preferredLanguage column on RegisteredUser (#505)
mustafa-5493 May 10, 2026
0236045
feat(users): expose preferredLanguage in UserProfileDTO (#505)
mustafa-5493 May 10, 2026
4b2b9a7
feat(users): only surface preferredLanguage on self-views, add update…
mustafa-5493 May 10, 2026
c70dbfc
feat(users): add UpdateLanguageRequest DTO (#505)
mustafa-5493 May 10, 2026
2e3ac0b
feat(users): add PUT /api/users/{id}/profile/language endpoint (#505)
mustafa-5493 May 10, 2026
45ef432
test(users): cover updateLanguage service paths and DTO privacy gate …
mustafa-5493 May 10, 2026
aeec868
chore(db): V4 migration for preferred_language column (#505)
mustafa-5493 May 10, 2026
2806cdb
fix(users): update AdminUserServiceTest constructors for new preferre…
mustafa-5493 May 10, 2026
fc2eefd
fix(users): drop unused findById stub from unknown-language test (#505)
mustafa-5493 May 10, 2026
6109d13
chore(db): bump preferred_language migration to V6 to avoid V4 collis…
mustafa-5493 May 11, 2026
1d99b8a
feat(web): add i18next + react-i18next + browser language detector (#…
mustafa-5493 May 10, 2026
6438f6f
feat(web): bootstrap i18n with en/tr resources and detector (#292)
mustafa-5493 May 10, 2026
b165b30
feat(web): wire i18n init at app entry (#292)
mustafa-5493 May 10, 2026
72412df
feat(web): add updateLanguage user service call (#292)
mustafa-5493 May 10, 2026
0a4fef4
feat(web): add useSetLanguage hook (UI flip + backend persist) (#292)
mustafa-5493 May 10, 2026
6fe4071
feat(web): restore persisted UI language on login (#292)
mustafa-5493 May 10, 2026
2f3fd73
feat(web): add LanguageSwitcher EN/TR toggle component (#292)
mustafa-5493 May 10, 2026
5ab751d
feat(web): mount LanguageSwitcher in Navbar (#292)
mustafa-5493 May 10, 2026
854ddc1
test(web): stub LanguageSwitcher in Navbar tests (#292)
mustafa-5493 May 10, 2026
b55617b
test(web): bootstrap i18next once for the vitest run (#292)
mustafa-5493 May 10, 2026
b25793d
feat(web): add navbar translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
3076db4
feat(web): translate Navbar via i18next (#292)
mustafa-5493 May 10, 2026
25c8496
feat(web): add auth (login + signup) translation keys (#292)
mustafa-5493 May 10, 2026
50bd1f0
feat(web): translate Login page via i18next (#292)
mustafa-5493 May 10, 2026
f350b48
feat(web): translate Signup page via i18next (#292)
mustafa-5493 May 10, 2026
5d59319
feat(web): add Home page translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
0935085
feat(web): translate Home page (zoom controls, FABs, community pulse)…
mustafa-5493 May 10, 2026
727c6d3
feat(web): add OnboardingTutorial translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
97b50a1
feat(web): translate OnboardingTutorial via i18next (#292)
mustafa-5493 May 10, 2026
7c5d9db
feat(web): add profile translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
aacac56
feat(web): translate ProfilePage and UserProfilePage via i18next (#292)
mustafa-5493 May 10, 2026
88e3a3b
feat(web): add Feed page translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
84cc12a
feat(web): translate Feed page (filters, status, cards, empty states)…
mustafa-5493 May 10, 2026
c62e447
feat(web): add CreateReportPanel + ReportPanel translation keys (en +…
mustafa-5493 May 10, 2026
2bcd2be
feat(web): translate CreateReportPanel via i18next (#292)
mustafa-5493 May 10, 2026
8c6194f
feat(web): translate ReportPanel aria-labels and vote errors via i18n…
mustafa-5493 May 10, 2026
0d942bc
feat(web): expand ReportPanel translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
9de1d13
feat(web): translate ReportPanel objects header, status pills, fix vo…
mustafa-5493 May 10, 2026
0a76217
feat(web): add MapSearchBar + map hint translation keys (en + tr) (#292)
mustafa-5493 May 10, 2026
5d84f89
feat(web): translate MapSearchBar (label, placeholder, errors) via i1…
mustafa-5493 May 10, 2026
2c989d4
feat(web): translate Home map pin-drop and route hints via i18next (#…
mustafa-5493 May 10, 2026
6605a0c
feat(web): translate 'Objects required' label in CreateReportPanel (#…
mustafa-5493 May 10, 2026
9984499
feat(web): add avatar, routing, myReports translation keys (en + tr) …
mustafa-5493 May 10, 2026
a24db5b
feat(web): translate AvatarUploader via i18next (#292)
mustafa-5493 May 10, 2026
a58b9cc
feat(web): translate MyReportsSection via i18next (#292)
mustafa-5493 May 10, 2026
e4a80ca
feat(web): translate RoutingPreferencesSection (preset names override…
mustafa-5493 May 10, 2026
cfd8263
feat(web): translate role badge (USER/ADMIN) on profile pages (#292)
mustafa-5493 May 10, 2026
df052c1
fix(web): tr β€” Rotalama β†’ Rota in routing prefs title (#292)
mustafa-5493 May 10, 2026
1dde347
feat(web): translate NotificationDropdown (header, empty, mark-all) v…
mustafa-5493 May 10, 2026
fdaf473
feat(web): translate UserSearchPage via i18next (#292)
mustafa-5493 May 10, 2026
2798856
feat(web): translate ReportPanel issue details, consensus, comments, …
mustafa-5493 May 10, 2026
cf1f7ff
feat(web): add object/issue/measurement labels + map-filter aria keys…
mustafa-5493 May 10, 2026
dc40567
feat(web): add localizeObjectType helper for translated label lookups…
mustafa-5493 May 10, 2026
c5ef472
feat(web): rebuild ReportPanel title and Objects section with localiz…
mustafa-5493 May 10, 2026
4ca47b3
feat(web): localize object/issue/measurement labels in CreateReportPa…
mustafa-5493 May 10, 2026
1cbeaee
feat(web): localize ObjectTutorialModal labels and aria-labels (#292)
mustafa-5493 May 10, 2026
b07af41
feat(web): localize MapFilters object/issue labels and aria-labels (#…
mustafa-5493 May 10, 2026
889316a
feat(web): localize ReportObjectsList labels via i18next (#292)
mustafa-5493 May 10, 2026
d431990
feat(web): translate notification timestamps (just now / m / h / d ag…
mustafa-5493 May 10, 2026
a3cc717
feat(web): translate MyReportDetailModal (title, status, env, actions…
mustafa-5493 May 10, 2026
4965c6b
fix(web-i18n): make LanguageSwitcher visible on mobile (#292)
mustafa-5493 May 11, 2026
a2c62e6
test(web-i18n): update ReportPanel media tests for translated aria-la…
mustafa-5493 May 11, 2026
7184dc3
feat(web-i18n): translate Leaderboard nav link (#292)
mustafa-5493 May 11, 2026
1f84953
feat(web-i18n): translate Leaderboard page (#292)
mustafa-5493 May 11, 2026
6215ed9
Merge pull request #533 from bounswe/feat/backend-user-language-pref
mustafa-5493 May 11, 2026
e55fe43
Merge pull request #534 from bounswe/feat/web-i18n-language-switcher
mustafa-5493 May 11, 2026
c92ae70
refactor(web): drop literal label/message from password rules util (#…
mustafa-5493 May 10, 2026
691172d
test(web): adjust passwordValidation tests after dropping literal str…
mustafa-5493 May 10, 2026
ee9970a
feat(web): add password rule translation keys (en + tr) (#546)
mustafa-5493 May 10, 2026
17880a0
feat(web): translate password rules via i18next on signup (#546)
mustafa-5493 May 10, 2026
5370cbd
Merge pull request #547 from bounswe/feat/web-i18n-password-rules-sta…
mustafa-5493 May 11, 2026
bcbc5ab
feat(web): add FeedLocationPickerModal translation keys (en + tr) (#548)
mustafa-5493 May 10, 2026
3ed7c9b
feat(web): translate FeedLocationPickerModal via i18next (#548)
mustafa-5493 May 10, 2026
df120f4
Merge pull request #549 from bounswe/feat/web-i18n-feed-picker
mustafa-5493 May 11, 2026
cfdaf67
feat(web): add CreateFixRequestPanel translation keys (en + tr) (#550)
mustafa-5493 May 10, 2026
16fc877
feat(web): translate CreateFixRequestPanel via i18next (#550)
mustafa-5493 May 10, 2026
3a06dcd
fix(web): pass localized displayTitle to CreateFixRequestPanel (#550)
mustafa-5493 May 10, 2026
3ddd9c6
Merge pull request #551 from bounswe/feat/web-i18n-create-fix-panel
mustafa-5493 May 11, 2026
6ab9572
feat(web): translate last hardcoded toast (report-submitted success) …
mustafa-5493 May 10, 2026
0e07da6
Merge pull request #558 from bounswe/feat/web-i18n-toasts
mustafa-5493 May 11, 2026
c78f269
feat(web): localize measurement descriptions in localizeObjectType he…
mustafa-5493 May 10, 2026
8a6864f
feat(web): add measurement description keys for all 9 object types (e…
mustafa-5493 May 10, 2026
7da6e8a
feat(web): translate ObjectTutorialModal threshold text, empty + foot…
mustafa-5493 May 10, 2026
d3736f4
feat(web): apiFetch sends Accept-Language from active i18n locale (#562)
mustafa-5493 May 10, 2026
d4344cc
feat(web): forward Accept-Language on raw fetch sites (avatar / media…
mustafa-5493 May 10, 2026
bf47c39
test(web): stub currentLanguageTag in api.js mocks (#562)
mustafa-5493 May 10, 2026
1cae576
Merge pull request #563 from bounswe/feat/web-i18n-accept-language-he…
mustafa-5493 May 11, 2026
88c5b05
feat(web-i18n): add locale keys for remaining hardcoded strings (#574)
mustafa-5493 May 11, 2026
f382b68
feat(web-i18n): translate RoutePanel hardcoded strings (#574)
mustafa-5493 May 11, 2026
f2c7c79
feat(web-i18n): translate ReportPanel comments login/loading/anonymou…
mustafa-5493 May 11, 2026
1c71528
feat(web-i18n): translate CustomPresetEditorModal name/travel-mode/er…
mustafa-5493 May 11, 2026
ad0ab08
feat(web-i18n): translate routing preferences loading state (#574)
mustafa-5493 May 11, 2026
43de470
feat(web-i18n): translate avatar remove confirm and error (#574)
mustafa-5493 May 11, 2026
c8940e3
feat(web-i18n): translate avatar upload error fallback (#574)
mustafa-5493 May 11, 2026
1a8bdb4
Merge pull request #575 from bounswe/feat/web-i18n-remaining-strings
mustafa-5493 May 11, 2026
5fc42ac
feat(backend): add EN + TR message bundles for user-facing errors (#560)
mustafa-5493 May 10, 2026
5d87024
feat(backend): wire MessageSource + AcceptHeader LocaleResolver (#560)
mustafa-5493 May 10, 2026
fe6a6a0
feat(users): resolve error messages via MessageSource against caller …
mustafa-5493 May 10, 2026
8bb463c
test(users): stub MessageSource so existing assertions still match (#…
mustafa-5493 May 10, 2026
f0e823a
Merge pull request #561 from bounswe/feat/backend-i18n-error-messages
mustafa-5493 May 11, 2026
40492a8
feat(web-i18n): translate MapFilters chrome (title, hidden pill, show…
mustafa-5493 May 11, 2026
b028b2e
feat(web-i18n): translate badge label and description (TRUSTED_REPORT…
mustafa-5493 May 11, 2026
ec826f4
feat(web-i18n): translate leaderboard visibility toasts
mustafa-5493 May 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.bounswe2026group1.backend.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;

import java.util.List;
import java.util.Locale;

/**
* Wires Spring's message-source + locale-resolver so user-facing error
* strings produced by services (e.g. {@code RegisteredUserService}) can be
* resolved against the caller's locale.
*
* Locale resolution: read {@code Accept-Language} header, default to English
* when missing or unsupported. Supported list is kept narrow (en, tr) so an
* unrecognised header doesn't surprise the user with another fallback.
*
* Note: an authenticated user's persisted {@code preferredLanguage} (#505)
* is currently honoured by clients sending the right {@code Accept-Language}
* header. If we ever want server-side resolution against the DB column, this
* is the spot to plug in a custom {@code LocaleResolver}.
*/
@Configuration
public class I18nConfig {

@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("messages");
source.setDefaultEncoding("UTF-8");
// Fall back through messages_xx -> messages.properties before failing,
// so a missing TR key returns the EN string rather than raising.
source.setFallbackToSystemLocale(false);
return source;
}

@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setSupportedLocales(List.of(Locale.ENGLISH, Locale.forLanguageTag("tr")));
resolver.setDefaultLocale(Locale.ENGLISH);
return resolver;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,33 @@ public ResponseEntity<Object> updateProfile(@PathVariable Long id,
}
}

@PutMapping("/{id}/profile/language")
@Operation(
summary = "Update the authenticated user's UI language preference",
description = "Sets the caller's preferred language (EN or TR). The path id must match the caller's id."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Updated profile (self-view, includes preferredLanguage)."),
@ApiResponse(responseCode = "400", description = "Unknown or blank language value."),
@ApiResponse(responseCode = "401", description = "Authentication required."),
@ApiResponse(responseCode = "403", description = "Path id does not match the caller."),
@ApiResponse(responseCode = "404", description = "No user with that id.")
})
@SecurityRequirement(name = OpenApiConfig.BEARER_SCHEME)
public ResponseEntity<Object> updateLanguage(@PathVariable Long id,
@RequestBody @Valid com.bounswe2026group1.backend.dto.UpdateLanguageRequest request) {
try {
requireOwner(id);
return ResponseEntity.ok(registeredUserService.updateLanguage(id, request));
} catch (AccessDeniedException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(e.getMessage());
} catch (NoSuchElementException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
}
}

@PostMapping(value = "/{id}/profile/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(
summary = "Upload a new avatar for the authenticated user",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.bounswe2026group1.backend.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Set the caller's UI language preference.")
public class UpdateLanguageRequest {

@Schema(description = "Language code matching the Language enum.",
example = "TR", allowableValues = {"EN", "TR"})
@NotBlank(message = "Language must not be blank")
private String language;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public class UserProfileDTO {
example = "false")
private boolean leaderboardHidden;

@Schema(description = "Preferred UI language. Null on public profile lookups; populated on self-views.",
example = "EN", allowableValues = {"EN", "TR"}, nullable = true)
private String preferredLanguage;

@Data
@Builder
@NoArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.bounswe2026group1.backend.model;

/**
* UI language preference persisted on {@link RegisteredUser}. New users
* default to {@link #EN}; clients send the wire form as the enum name
* ("EN" / "TR") in {@code UpdateLanguageRequest}.
*/
public enum Language {
EN, TR
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ public class RegisteredUser {
columnDefinition = "BOOLEAN DEFAULT FALSE")
private boolean leaderboardHidden = false;

/**
* UI language preference. Web and mobile read this on login so the
* choice survives a fresh session on another device. Declared last to
* keep the Lombok all-args constructor backwards-compatible.
*/
@Enumerated(EnumType.STRING)
@Column(name = "preferred_language", nullable = false, length = 8,
columnDefinition = "VARCHAR(8) DEFAULT 'EN'")
private Language preferredLanguage = Language.EN;

@PrePersist
protected void onCreate() {
if (this.registeredAt == null) {
Expand All @@ -135,5 +145,8 @@ protected void onCreate() {
if (this.routingConstraints == null) {
this.routingConstraints = new HashSet<>();
}
if (this.preferredLanguage == null) {
this.preferredLanguage = Language.EN;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import com.bounswe2026group1.backend.repository.UserBadgeRepository;
import com.bounswe2026group1.backend.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.authentication.BadCredentialsException;
Expand All @@ -43,20 +45,26 @@ public class RegisteredUserService {
private final LeaderboardService leaderboardService;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
private final MessageSource messageSource;

// Password strength regex pattern: Minimum 8 characters, at least one uppercase letter, one lowercase letter, one digit and one special character
private static final String PASSWORD_PATTERN = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=!]).{8,}$";

/** Resolve an i18n message against the request's locale (see {@link com.bounswe2026group1.backend.config.I18nConfig}). */
private String msg(String code, Object... args) {
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}


public RegisterResponse registerUser(RegisterRequest request) {
// 1. Check Uniqueness
if (registeredUserRepository.existsByEmail(request.getEmail())) {
throw new IllegalArgumentException("Email is already in use.");
throw new IllegalArgumentException(msg("error.email.alreadyInUse"));
}

// 2. Password strength validation
if (!request.getPassword().matches(PASSWORD_PATTERN)) {
throw new IllegalArgumentException("Password must be at least 8 characters long, and include an uppercase letter, a lowercase letter, a digit, and a special character.");
throw new IllegalArgumentException(msg("error.password.weak"));
}

// 3. User Entity Conversion & Password Hashing
Expand All @@ -82,11 +90,11 @@ public RegisterResponse registerUser(RegisterRequest request) {
public LoginResponse loginUser(LoginRequest request) {
// 1. Find user by email
RegisteredUser user = registeredUserRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new BadCredentialsException("Invalid email or password"));
.orElseThrow(() -> new BadCredentialsException(msg("error.auth.invalidCredentials")));

// 2. Check password
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new BadCredentialsException("Invalid email or password");
throw new BadCredentialsException(msg("error.auth.invalidCredentials"));
}

// 3. Generate JWT token
Expand Down Expand Up @@ -144,7 +152,7 @@ public List<UserProfileDTO> getAllProfiles() {
* Email is omitted in the DTO (privacy) β€” search results are public. */
public Page<UserProfileDTO> searchUsers(String query, Pageable pageable) {
if (query == null || query.isBlank()) {
throw new IllegalArgumentException("Search query must not be empty.");
throw new IllegalArgumentException(msg("error.search.emptyQuery"));
}
String trimmed = query.trim();
Page<RegisteredUser> page = registeredUserRepository
Expand Down Expand Up @@ -225,6 +233,25 @@ public UserProfileDTO setLeaderboardHidden(Long id, boolean hidden) {
return toProfileDTO(saved, true);
}

/**
* Owner-only language preference update (#505). Rejects unknown enum
* values with {@link IllegalArgumentException} so the controller can
* return 400.
*/
public UserProfileDTO updateLanguage(Long id, com.bounswe2026group1.backend.dto.UpdateLanguageRequest request) {
com.bounswe2026group1.backend.model.Language lang;
try {
lang = com.bounswe2026group1.backend.model.Language.valueOf(request.getLanguage().toUpperCase());
} catch (IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException("Unknown language: " + request.getLanguage());
}
RegisteredUser user = registeredUserRepository.findById(id)
.orElseThrow(() -> new NoSuchElementException("User not found with id: " + id));
user.setPreferredLanguage(lang);
RegisteredUser saved = registeredUserRepository.save(user);
return toProfileDTO(saved, true);
}

/** All badges currently held by a user β€” exposed as its own endpoint so
* the public profile page can fetch badges without going through the
* full profile DTO. */
Expand Down Expand Up @@ -284,6 +311,11 @@ private UserProfileDTO buildDTO(RegisteredUser user,
.badges(badges)
.topBadge(Badge.pickHighestTier(badges))
.leaderboardHidden(user.isLeaderboardHidden())
// Same privacy gate as email: only surface the language preference
// on self-views so it doesn't leak through public lookups.
.preferredLanguage(includeEmail && user.getPreferredLanguage() != null
? user.getPreferredLanguage().name()
: null)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- Adds the per-user UI language preference (#505).
-- Hibernate's ddl-auto=update creates this column on fresh dev databases, but
-- the Flyway migration is the source of truth for prod/CI databases that
-- already have the schema baselined.

ALTER TABLE registered_users
ADD COLUMN IF NOT EXISTS preferred_language VARCHAR(8) NOT NULL DEFAULT 'EN';
14 changes: 14 additions & 0 deletions backend/src/main/resources/messages.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# User-facing error and validation messages. Keep in sync with
# messages_tr.properties and any other locale files.
#
# Resolved via MessageSource at the point an exception is thrown so the
# caller's locale (Accept-Language header, falling back to the authenticated
# user's preferredLanguage) controls the rendered language.

error.email.alreadyInUse=Email is already in use.
error.password.weak=Password must be at least 8 characters long, and include an uppercase letter, a lowercase letter, a digit, and a special character.
error.auth.invalidCredentials=Invalid email or password
error.user.notFoundById=User not found with id: {0}
error.user.notFoundByEmail=User not found with email: {0}
error.search.emptyQuery=Search query must not be empty.
error.language.unknown=Unknown language: {0}
10 changes: 10 additions & 0 deletions backend/src/main/resources/messages_tr.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Turkish translations of user-facing error messages.
# Keep keys in sync with messages_en.properties.

error.email.alreadyInUse=Bu e-posta zaten kullanΔ±mda.
error.password.weak=Şifre en az 8 karakter olmalı ve büyük harf, küçük harf, rakam ve âzel karakter içermeli.
error.auth.invalidCredentials=Geçersiz e-posta veya şifre
error.user.notFoundById=ID ile kullanΔ±cΔ± bulunamadΔ±: {0}
error.user.notFoundByEmail=E-posta ile kullanΔ±cΔ± bulunamadΔ±: {0}
error.search.emptyQuery=Arama sorgusu boş olamaz.
error.language.unknown=Bilinmeyen dil: {0}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.bounswe2026group1.backend.dto.admin.AdminChangeRoleRequest;
import com.bounswe2026group1.backend.dto.admin.AdminCreateUserRequest;
import com.bounswe2026group1.backend.dto.admin.AdminUserResponse;
import com.bounswe2026group1.backend.model.Language;
import com.bounswe2026group1.backend.model.RegisteredUser;
import com.bounswe2026group1.backend.model.UserRole;
import com.bounswe2026group1.backend.model.UserStatus;
Expand Down Expand Up @@ -44,9 +45,9 @@ class AdminUserServiceTest {
@BeforeEach
void setUp() {
adminUser = new RegisteredUser(1L, "Admin", "admin@test.com", "hash", UserRole.ADMIN,
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false);
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false, Language.EN);
regularUser = new RegisteredUser(2L, "User", "user@test.com", "hash", UserRole.USER,
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false);
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false, Language.EN);
}

// --- listUsers ---
Expand Down Expand Up @@ -170,7 +171,7 @@ void changeRole_Fail_SelfDemotion() {
@Test
void changeRole_Fail_LastAdmin() {
RegisteredUser anotherAdmin = new RegisteredUser(3L, "Admin2", "admin2@test.com", "hash", UserRole.ADMIN,
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false);
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false, Language.EN);
when(userRepository.findById(3L)).thenReturn(Optional.of(anotherAdmin));
when(userRepository.countByRole(UserRole.ADMIN)).thenReturn(1L);

Expand All @@ -190,7 +191,7 @@ void createUser_Success() {
when(userRepository.existsByEmail("new@test.com")).thenReturn(false);
when(passwordEncoder.encode(any())).thenReturn("encoded");
RegisteredUser saved = new RegisteredUser(5L, "New", "new@test.com", "encoded", UserRole.USER,
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false);
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false, Language.EN);
when(userRepository.save(any())).thenReturn(saved);

AdminCreateUserRequest req = new AdminCreateUserRequest();
Expand Down Expand Up @@ -249,7 +250,7 @@ void deleteUser_Fail_SelfDelete() {
@Test
void deleteUser_Fail_LastAdmin() {
RegisteredUser anotherAdmin = new RegisteredUser(3L, "Admin2", "admin2@test.com", "hash", UserRole.ADMIN,
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false);
UserStatus.ACTIVE, null, 0, null, null, null, null, null, null, false, Language.EN);
when(userRepository.findById(3L)).thenReturn(Optional.of(anotherAdmin));
when(userRepository.countByRole(UserRole.ADMIN)).thenReturn(1L);

Expand Down
Loading
Loading