Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
|
|
|
|
'back' => '뒤로',
|
|
|
|
|
|
|
|
|
|
'confirm_modal' => [
|
|
|
|
|
'cannot_be_undone' => '이 작업은 되돌릴 수 없습니다.',
|
|
|
|
|
'type' => '입력',
|
|
|
|
|
'to_confirm' => '하여 확인하세요.',
|
|
|
|
|
'copy_to_clipboard' => '클립보드에 복사',
|
|
|
|
|
'delete_keyword' => '삭제',
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'photo_upload' => [
|
|
|
|
|
'upload' => '업로드',
|
|
|
|
|
'uploading' => '업로드 중...',
|
|
|
|
|
'remove' => '사진 제거',
|
|
|
|
|
'hint' => '권장: 정사각형 이미지, 최대 2MB.',
|
Crop the avatar/logo before upload with a dependency-free cropper
Selecting an avatar or workspace logo now opens a crop dialog (drag + zoom)
before uploading, so the image is framed the way it renders. The crop is
performed client-side and the resized 512x512 result is what gets uploaded.
This reworks the idea from #131 without its cropper dependency: vue-advanced-cropper
was last released ~2 years ago and we did not want an unmaintained package for
something this load-bearing. What we need is narrow (fixed 1:1, a circle/square
mask, fixed-size output), so a small canvas-based cropper covers it:
- imageCrop.ts: pure transform math (cover-fit, clamp, zoom, viewport->source).
- ImageCropperDialog.vue: CSS-transform preview, pointer drag, wheel/button zoom,
a ResizeObserver to measure the modal (no requestAnimationFrame timing hacks),
and a canvas toBlob only on save.
- PhotoUpload.vue: opens the cropper on file select; the mask shape follows the
display shape (round avatar / square logo) instead of always being round.
- crop_* strings added to all 15 locales.
Also installs Pest browser testing (pest-plugin-browser + Playwright) and adds a
browser test for the crop flow. TestCase only calls withoutVite() for non-browser
tests, since browser tests need the real Vite assets to boot the SPA. The Pest
browser server does not parse multipart uploads, so the test asserts the crop
dispatches the correct upload request; endpoint persistence stays covered by
ProfileUpdateTest.
2026-07-04 00:46:19 +00:00
|
|
|
'crop_title' => '이미지 자르기',
|
2026-07-04 12:56:31 +00:00
|
|
|
'crop_description' => '남길 영역을 선택하세요.',
|
|
|
|
|
'crop_hint' => '드래그하여 이동하거나 모서리로 크기를 조절하세요.',
|
Crop the avatar/logo before upload with a dependency-free cropper
Selecting an avatar or workspace logo now opens a crop dialog (drag + zoom)
before uploading, so the image is framed the way it renders. The crop is
performed client-side and the resized 512x512 result is what gets uploaded.
This reworks the idea from #131 without its cropper dependency: vue-advanced-cropper
was last released ~2 years ago and we did not want an unmaintained package for
something this load-bearing. What we need is narrow (fixed 1:1, a circle/square
mask, fixed-size output), so a small canvas-based cropper covers it:
- imageCrop.ts: pure transform math (cover-fit, clamp, zoom, viewport->source).
- ImageCropperDialog.vue: CSS-transform preview, pointer drag, wheel/button zoom,
a ResizeObserver to measure the modal (no requestAnimationFrame timing hacks),
and a canvas toBlob only on save.
- PhotoUpload.vue: opens the cropper on file select; the mask shape follows the
display shape (round avatar / square logo) instead of always being round.
- crop_* strings added to all 15 locales.
Also installs Pest browser testing (pest-plugin-browser + Playwright) and adds a
browser test for the crop flow. TestCase only calls withoutVite() for non-browser
tests, since browser tests need the real Vite assets to boot the SPA. The Pest
browser server does not parse multipart uploads, so the test asserts the crop
dispatches the correct upload request; endpoint persistence stays covered by
ProfileUpdateTest.
2026-07-04 00:46:19 +00:00
|
|
|
'crop_save' => '저장',
|
|
|
|
|
'crop_cancel' => '취소',
|
2026-07-04 01:07:23 +00:00
|
|
|
'crop_error' => '이 이미지를 불러올 수 없습니다. 다른 파일을 사용해 보세요.',
|
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'timezone' => [
|
|
|
|
|
'select' => '시간대 선택',
|
|
|
|
|
'search' => '시간대 검색...',
|
|
|
|
|
'empty' => '시간대를 찾을 수 없습니다',
|
|
|
|
|
],
|
|
|
|
|
|
2026-08-06 00:02:34 +00:00
|
|
|
'just_now' => '방금',
|
|
|
|
|
|
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
|
|
|
'date_picker' => [
|
2026-08-06 00:02:34 +00:00
|
|
|
'label' => '날짜 선택기',
|
Translate the interface into 12 languages
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
2026-07-03 18:38:40 +00:00
|
|
|
'select' => '날짜 선택',
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'date_range_picker' => [
|
|
|
|
|
'placeholder' => '날짜 범위 선택',
|
|
|
|
|
'today' => '오늘',
|
|
|
|
|
'yesterday' => '어제',
|
|
|
|
|
'last_7_days' => '지난 7일',
|
|
|
|
|
'last_30_days' => '지난 30일',
|
|
|
|
|
'last_3_months' => '지난 3개월',
|
|
|
|
|
'last_6_months' => '지난 6개월',
|
|
|
|
|
'last_12_months' => '지난 12개월',
|
|
|
|
|
'this_month' => '이번 달',
|
|
|
|
|
'last_month' => '지난달',
|
|
|
|
|
'year_to_date' => '올해 누적',
|
|
|
|
|
'last_year' => '작년',
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
'cancel' => '취소',
|
|
|
|
|
'clear' => '지우기',
|
|
|
|
|
'close' => '닫기',
|
|
|
|
|
'loading_more' => '더 불러오는 중...',
|
|
|
|
|
|
|
|
|
|
'actions' => [
|
|
|
|
|
'copy' => '복사',
|
|
|
|
|
'copied' => '복사됨',
|
|
|
|
|
'copy_failed' => '클립보드에 복사하지 못했습니다',
|
|
|
|
|
],
|
|
|
|
|
];
|