diff --git a/flutter/lib/core/constants/login_texts.dart b/flutter/lib/core/constants/login_texts.dart index 6f4e0d8..564b54a 100644 --- a/flutter/lib/core/constants/login_texts.dart +++ b/flutter/lib/core/constants/login_texts.dart @@ -14,7 +14,7 @@ abstract final class LoginTexts { static const String brandHeadline = '사내 업무를 한 계정으로'; /// 브랜드 띠의 소개 글 - static const String brandLead = '메일부터 시작해 캘린더와 파일 저장소까지 같은 계정으로 이어집니다.'; + static const String brandLead = '메일부터 시작해 캘린더와 파일 저장소까지 같은 계정으로 쓸 수 있게 됩니다.'; static const String subtitle = '회사 메일 주소(@$kCompanyMailDomain)로 로그인해 주세요.'; @@ -44,7 +44,7 @@ abstract final class LoginBandTexts { abstract final class SupportLineTexts { /// 첫 단계 static const String passwordQuestion = '비밀번호를 잊으셨나요?'; - static const String passwordFallback = '비밀번호를 잊으셨다면 시스템 관리자에게 문의해 주세요.'; + static const String passwordFallback = '비밀번호를 잊으셨다면 관리자에게 문의해 주세요.'; /// 코드 단계 · 2단계 인증 켜기 static const String authenticatorQuestion = '인증 앱을 쓸 수 없나요?'; @@ -110,7 +110,7 @@ abstract final class PasswordChangeTexts { /// 넷째 단계 — 2단계 인증 켜기 (제한 `twoFactorSetup`). 같은 휴대폰이라 QR 대신 [인증 앱에 추가] · 키 · [복사] abstract final class TwoFactorSetupTexts { static const String subtitle = - '회사 정책에 따라 로그인할 때 휴대폰 인증 앱의 코드도 확인합니다. 지금 켜면 바로 메일로 이어집니다.'; + '회사 정책에 따라 로그인할 때 휴대폰 인증 앱의 코드도 확인합니다. 지금 켜면 바로 메일을 쓸 수 있습니다.'; /// ① 인증 앱에 추가 static const String addTitle = '인증 앱에 추가해 주세요'; diff --git a/flutter/lib/features/mail/presentation/mail_texts.dart b/flutter/lib/features/mail/presentation/mail_texts.dart index 980a60b..67b0262 100644 --- a/flutter/lib/features/mail/presentation/mail_texts.dart +++ b/flutter/lib/features/mail/presentation/mail_texts.dart @@ -211,7 +211,7 @@ abstract final class MailReaderTexts { // ── 안내 줄 ── static const String imagesBlocked = - '외부 이미지를 차단했습니다. 개인정보 보호를 위해 보낸 사람 서버의 이미지는 불러오지 않습니다.'; + '외부 이미지는 막아 두었습니다. 이미지를 불러오면 보낸 사람이 내가 메일을 열어 본 것을 알 수 있습니다.'; static const String showImages = '이미지 보기'; /// 그림을 받아 넣는 동안 — 화면 낭독기 (앱에서 새로 씀) @@ -219,9 +219,11 @@ abstract final class MailReaderTexts { /// 본문 안내 (웹 `MAIL_BODY_NOTICES`) static String bodyNotice(MailBodyNotice notice) => switch (notice) { - MailBodyNotice.htmlOmitted => '본문이 복잡해 서식 없이 글자만 표시합니다.', - MailBodyNotice.unavailable => '본문이 너무 크거나 복잡해 표시할 수 없습니다.', - MailBodyNotice.truncated => '본문이 길어 앞부분만 표시합니다.', + MailBodyNotice.htmlOmitted => '본문이 복잡해 서식 없이 글자만 보여 드립니다.', + MailBodyNotice.unavailable => + '본문이 너무 크거나 복잡해 앱에서는 열 수 없습니다. PC 웹메일에서 원문을 내려받아 볼 수 있습니다.', + MailBodyNotice.truncated => + '본문이 길어 앞부분만 보여 드립니다. 전체는 PC 웹메일에서 원문을 내려받아 볼 수 있습니다.', }; /// 보내기 취소를 기다리는 메일 (앱에서 새로 씀 — 웹 읽기 화면에는 이 줄이 없다) @@ -365,15 +367,16 @@ abstract final class MailListTexts { static String emptyTitle(String mailboxName) => '$mailboxName${subjectParticle(mailboxName)} 비어 있습니다'; - /// 메일함마다 빈 메일함 안내 — 키 null 은 사용자가 만든 메일함 + /// 메일함마다 빈 메일함 안내 — 키 null 은 사용자가 만든 메일함. + /// 그 글은 앱 글이다(웹과 다르다) — 앱의 [이동]은 글 없는 아이콘이고 고르기는 길게 누르기라서(2026-11-07) static String emptyHint(MailboxRole? role) => switch (role) { - MailboxRole.inbox => '새 메일이 오면 여기에 표시됩니다.', - MailboxRole.drafts => '임시 저장한 메일이 여기에 표시됩니다.', - MailboxRole.sent => '보낸 메일이 여기에 표시됩니다.', - MailboxRole.junk => '스팸으로 분류된 메일이 여기에 표시됩니다. 들어간 지 30일이 지난 메일은 자동으로 지워집니다.', - MailboxRole.trash => '삭제한 메일이 여기에 표시됩니다. 들어간 지 30일이 지난 메일은 자동으로 지워집니다.', - MailboxRole.archive => '보관한 메일이 여기에 표시됩니다.', - null => '이 메일함으로 옮긴 메일이 여기에 표시됩니다.', + MailboxRole.inbox => '새 메일이 오면 여기로 들어옵니다.', + MailboxRole.drafts => '쓰다가 임시 저장한 메일은 여기에 남습니다.', + MailboxRole.sent => '보낸 메일은 여기에 남습니다.', + MailboxRole.junk => '스팸 메일은 여기에 30일 동안 남아 있다가 저절로 지워집니다.', + MailboxRole.trash => '지운 메일은 여기에 30일 동안 남아 있다가 저절로 지워집니다.', + MailboxRole.archive => '오래 두고 볼 메일을 여기로 옮겨 보관할 수 있습니다.', + null => '다른 메일함에서 메일을 길게 눌러 고른 뒤 이 메일함으로 옮길 수 있습니다.', }; /// 휴지통 · 스팸메일함 목록 위의 30일 줄 (웹 13단계 — 메일이 없으면 빈 안내에 붙인다) @@ -428,15 +431,16 @@ abstract final class MailSearchTexts { // ── 안내 줄 (웹 `SEARCH_NOTICE_TEXT`) ── /// 지난 메일의 색인을 만드는 동안(`GET /search-index` 의 building) static const String indexing = - '지난 메일을 검색할 수 있게 밤사이에 준비하고 있습니다. 메일이 많으면 며칠 걸릴 수 있고, 그동안은 낱말이 통째로 같은 메일만 찾습니다.'; + '지난 메일까지 낱말의 일부로도 찾을 수 있게 밤사이에 준비하고 있습니다. 메일이 많으면 며칠 걸릴 수 있고, 그동안은 낱말을 끝까지 다 써야 찾을 수 있습니다.'; /// 한 글자 낱말뿐이라 본문은 찾지 않았을 때(색인이 준비됐을 때만) static const String singleChar = '한 글자로만 찾을 때는 본문은 빼고 제목, 보낸 사람과 받는 사람, 첨부 파일 이름에서 찾습니다. 본문까지 찾으려면 두 글자 이상으로 찾아 주세요.'; // ── 결과 없음 ── - /// "‘해지 통보’에 맞는 메일이 없습니다" - static String emptyTitle(String query) => '‘$query’에 맞는 메일이 없습니다'; + /// "‘해지 통보’가 들어간 메일이 없습니다" + static String emptyTitle(String query) => + '‘$query’${subjectParticle(query)} 들어간 메일이 없습니다'; /// 검색어만으로 찾았을 때의 도움말 (웹 `emptyWordsHint` — 조건은 2차) static const String emptyHint = '다른 말로 찾아 보세요.'; @@ -449,7 +453,7 @@ abstract final class MailSearchTexts { static const String searchAll = '전체 메일함에서 찾기'; // ── 아직 못 찾음 · 목록 끝 ── - static const String partialTitle = '아직 맞는 메일을 찾지 못했습니다'; + static const String partialTitle = '아직 찾은 메일이 없습니다'; static const String partialHint = '메일이 많아 한 번에 다 찾지 못했습니다. [더 찾기]를 누르면 이어서 찾습니다.'; static const String more = '더 찾기'; @@ -460,9 +464,9 @@ abstract final class MailSearchTexts { // ── 찾을 말이 너무 많음 ── static const String tooManyWordsTitle = '찾을 말이 너무 많습니다'; - /// (앱 1차 — 검색어만) 2차(조건)부터는 웹 글 "검색어와 조건에 넣은 낱말은 모두 합쳐 …" + /// (앱 1차 — 검색어만) 2차(조건)부터는 웹 글 "검색 칸과 [조건]에 넣은 낱말은 모두 합쳐 …" static String tooManyWordsHint(int max) => - '검색어의 낱말은 ${formatCount(max)}개까지 찾을 수 있습니다. 낱말을 줄여 주세요.'; + '검색어에는 낱말을 ${formatCount(max)}개까지 넣을 수 있습니다. 낱말을 몇 개 줄여 주세요.'; // ── 찾기 전 ── /// 처음 연 화면 — 무엇을 찾는지 (앱에서 새로 씀) diff --git a/flutter/lib/features/mail/presentation/widgets/mail_search_states.dart b/flutter/lib/features/mail/presentation/widgets/mail_search_states.dart index eb91f13..e928c1f 100644 --- a/flutter/lib/features/mail/presentation/widgets/mail_search_states.dart +++ b/flutter/lib/features/mail/presentation/widgets/mail_search_states.dart @@ -36,7 +36,7 @@ class MailSearchIntro extends StatelessWidget { } } -/// 결과 없음 — "‘해지 통보’에 맞는 메일이 없습니다" · 도움말 · (색인을 준비하는 중이면) 옛 한계 한 줄 · (한 메일함에서 찾았으면) [전체 메일함에서 찾기] +/// 결과 없음 — "‘해지 통보’가 들어간 메일이 없습니다" · 도움말 · (색인을 준비하는 중이면) 옛 한계 한 줄 · (한 메일함에서 찾았으면) [전체 메일함에서 찾기] class MailSearchEmpty extends StatelessWidget { const MailSearchEmpty({ required this.query, diff --git a/flutter/test/features/mail/mail_list_page_test.dart b/flutter/test/features/mail/mail_list_page_test.dart index 4f320ce..fbbc95d 100644 --- a/flutter/test/features/mail/mail_list_page_test.dart +++ b/flutter/test/features/mail/mail_list_page_test.dart @@ -676,7 +676,7 @@ void main() { expect(findCopy('휴지통이 비어 있습니다'), findsOneWidget); expect( - findCopy('삭제한 메일이 여기에 표시됩니다. 들어간 지 30일이 지난 메일은 자동으로 지워집니다.'), + findCopy(MailListTexts.emptyHint(MailboxRole.trash)), findsOneWidget, ); expect(find.byType(MailRetentionNotice), findsNothing); diff --git a/flutter/test/features/mail/mail_rules_test.dart b/flutter/test/features/mail/mail_rules_test.dart index d87ebd7..9963852 100644 --- a/flutter/test/features/mail/mail_rules_test.dart +++ b/flutter/test/features/mail/mail_rules_test.dart @@ -86,6 +86,19 @@ void main() { expect(subjectParticle('Inbox'), '이(가)'); expect(MailListTexts.emptyTitle('휴지통'), '휴지통이 비어 있습니다'); expect(MailListTexts.emptyTitle('프로젝트'), '프로젝트가 비어 있습니다'); + // 검색 결과 없음 — 따옴표로 묶은 검색어 뒤에 붙인다 + expect( + MailSearchTexts.emptyTitle('계약'), + startsWith('${MailSearchTexts.quoted('계약')}이 '), + ); + expect( + MailSearchTexts.emptyTitle('해지 통보'), + startsWith('${MailSearchTexts.quoted('해지 통보')}가 '), + ); + expect( + MailSearchTexts.emptyTitle('ARA'), + startsWith('${MailSearchTexts.quoted('ARA')}이(가) '), + ); }); test('으로/로 — 받침 · ㄹ 받침 · 받침 없음 · 한글이 아님', () {