generated from ttipo/project-base
fix(flutter): 앱 화면 문구 18곳을 사람이 말하듯 다듬는다
2026-11-07 사용자가 "자연스러운 문구가 아니라 시스템, 설계 문구 형식"이라고
짚은 글을 웹과 같은 기준으로 고친다. 화면이 하는 일을 설명하는 말("여기에
표시됩니다")과 번역투("맞는 메일")를 빼고, 빈 메일함 안내 · 읽기 화면 본문 안내 ·
검색 글 · 로그인 글을 내 메일이 어떻게 되는지와 할 일로 쓴다. 본문을 다 보이지
못할 때는 PC 웹메일에서 원문을 받는 길까지 적고, 사용자가 만든 메일함의 빈 안내는
앱의 [이동]이 아이콘이라 길게 눌러 고르는 말로 쓴다. 오류 사전은 이 브랜치를 main
위로 옮길 때 새 글로 다시 만든다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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 = '인증 앱에 추가해 주세요';
|
||||
|
||||
@@ -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)}개까지 넣을 수 있습니다. 낱말을 몇 개 줄여 주세요.';
|
||||
|
||||
// ── 찾기 전 ──
|
||||
/// 처음 연 화면 — 무엇을 찾는지 (앱에서 새로 씀)
|
||||
|
||||
@@ -36,7 +36,7 @@ class MailSearchIntro extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// 결과 없음 — "‘해지 통보’에 맞는 메일이 없습니다" · 도움말 · (색인을 준비하는 중이면) 옛 한계 한 줄 · (한 메일함에서 찾았으면) [전체 메일함에서 찾기]
|
||||
/// 결과 없음 — "‘해지 통보’가 들어간 메일이 없습니다" · 도움말 · (색인을 준비하는 중이면) 옛 한계 한 줄 · (한 메일함에서 찾았으면) [전체 메일함에서 찾기]
|
||||
class MailSearchEmpty extends StatelessWidget {
|
||||
const MailSearchEmpty({
|
||||
required this.query,
|
||||
|
||||
@@ -676,7 +676,7 @@ void main() {
|
||||
|
||||
expect(findCopy('휴지통이 비어 있습니다'), findsOneWidget);
|
||||
expect(
|
||||
findCopy('삭제한 메일이 여기에 표시됩니다. 들어간 지 30일이 지난 메일은 자동으로 지워집니다.'),
|
||||
findCopy(MailListTexts.emptyHint(MailboxRole.trash)),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(MailRetentionNotice), findsNothing);
|
||||
|
||||
@@ -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('으로/로 — 받침 · ㄹ 받침 · 받침 없음 · 한글이 아님', () {
|
||||
|
||||
Reference in New Issue
Block a user