72d5ce1c3e
Opening one of the folder's own photos and hitting SAVE PHOTO used to make a second copy of it. Now it replaces that row — same id, same place — and the look the row carried steps into its history, newest first and capped at three, because the pixels it described are gone. The frame's own column in MY PHOTOS lists those looks (click one to put its settings back on the stage) and carries the landing-page consent as a plain tick, which answers the click at once. A file from the disk clears the open id, so a fresh frame still adds one.
247 lines
9.8 KiB
TypeScript
247 lines
9.8 KiB
TypeScript
// Vietnamese dictionary — the source of truth for key names (`en.ts` is typed
|
|
// as `Dict`, so a missing or misspelled key there is a typecheck error).
|
|
// Not translated anywhere, in any language: film-sim names, user recipe names,
|
|
// user watermark text — they are proper nouns, see WEB_PLAN.md.
|
|
|
|
export const vi = {
|
|
'lang.name': 'Tiếng Việt',
|
|
|
|
'nav.demo': 'DÙNG THỬ',
|
|
'nav.login': 'ĐĂNG NHẬP',
|
|
'nav.signup': 'ĐĂNG KÝ',
|
|
'nav.logout': 'Đăng xuất',
|
|
'nav.account': 'Tài khoản',
|
|
'nav.theme': 'Giao diện',
|
|
'nav.language': 'Ngôn ngữ',
|
|
'nav.back': 'Về trang chủ',
|
|
'nav.admin': 'Quản trị',
|
|
'nav.photos': 'Ảnh của tôi',
|
|
'nav.profile': 'Hồ sơ',
|
|
'nav.studio': 'Studio',
|
|
'nav.guest': 'Khách',
|
|
|
|
'act.undo': 'HOÀN TÁC',
|
|
'act.reset': 'ĐẶT LẠI',
|
|
'act.export': 'XUẤT ẢNH',
|
|
'act.exporting': 'Đang xuất…',
|
|
'act.cancel': 'Huỷ',
|
|
|
|
'drop.title': 'Kéo & thả ảnh vào đây',
|
|
'drop.hint': 'hoặc bấm để chọn ảnh · dán từ clipboard bằng Ctrl+V',
|
|
'drop.replace': 'Đổi ảnh khác',
|
|
'drop.peeking': 'ĐANG XEM ẢNH GỐC',
|
|
'drop.peekHint': 'Giữ chuột phải hoặc phím \\ để so ảnh gốc',
|
|
'drop.compare': 'So với ảnh gốc',
|
|
'drop.demoLimit': 'Bản demo: ảnh xuất ra tối đa {px}px và có watermark.',
|
|
|
|
// The histogram overlay. 'HISTOGRAM' is the word Vietnamese photographers use.
|
|
'hist.title': 'HISTOGRAM',
|
|
'hist.drag': 'Kéo để di chuyển',
|
|
'hist.hide': 'Ẩn histogram',
|
|
|
|
'tab.presets': 'PRESETS',
|
|
'tab.favorited': 'FAVORITED',
|
|
'tab.photos': 'ẢNH CỦA TÔI',
|
|
'tab.light': 'LIGHT',
|
|
'tab.wb': 'WB',
|
|
'tab.fx': 'FX',
|
|
'tab.frame': 'FRAME',
|
|
'tab.create': 'CREATE RECIPES',
|
|
|
|
'create.title': 'TẠO CÔNG THỨC',
|
|
'create.save': 'LƯU CÔNG THỨC',
|
|
'create.export': 'XUẤT TỆP .RECIPE',
|
|
'create.guestHint':
|
|
'Khách: công thức chỉ nằm trong RECIPES tới khi đóng trang. Đăng nhập để lưu vào tài khoản và xuất tệp.',
|
|
'create.userHint':
|
|
'Đã lưu vào tài khoản. Xuất tệp .recipe để chia sẻ — tệp được mã hóa đúng như trên app.',
|
|
|
|
'sec.filmSims': 'PHOTO STYLE',
|
|
'sec.recipes': 'RECIPES',
|
|
'sec.saved': 'ĐÃ LƯU',
|
|
'sec.savedEmpty': 'Chưa lưu công thức nào. Đăng nhập rồi bấm LƯU để đồng bộ.',
|
|
'sec.frame': 'KHUNG',
|
|
'sec.crop': 'CẮT',
|
|
'sec.rotate': 'XOAY',
|
|
'sec.watermark': 'WATERMARK',
|
|
'sec.gps': 'GPS',
|
|
|
|
'wb.temp': 'COLOR TEMP',
|
|
|
|
'frame.none': 'KHÔNG KHUNG',
|
|
'frame.polaroid': 'RETRO INSTANT',
|
|
'frame.classic': 'VIỀN CLASSIC',
|
|
'frame.cinematic': 'ĐIỆN ẢNH',
|
|
'frame.wall': 'WALL FRAME PORTRAIT',
|
|
'frame.wallLand': 'WALL FRAME LANDSCAPE',
|
|
|
|
'crop.none': 'KHÔNG CẮT',
|
|
'crop.1:1': '1:1',
|
|
'crop.2:3': '2:3',
|
|
'crop.3:2': '3:2',
|
|
'crop.3:4': '3:4',
|
|
'crop.4:3': '4:3',
|
|
'crop.16:9': '16:9',
|
|
'crop.free': 'TỰ DO',
|
|
'crop.apply': 'ÁP DỤNG',
|
|
'crop.cancel': 'HUỶ',
|
|
|
|
'rot.0': '0°',
|
|
'rot.90': '90°',
|
|
'rot.180': '180°',
|
|
'rot.270': '270°',
|
|
|
|
'wm.text': 'CHỮ',
|
|
'wm.color': 'MÀU',
|
|
'wm.size': 'CỠ',
|
|
'wm.gps': 'DẤU GPS',
|
|
'wm.place': 'TÊN ĐỊA ĐIỂM',
|
|
'wm.placePh': 'VD: HÀ NỘI',
|
|
'wm.manual': 'NHẬP TOẠ ĐỘ',
|
|
'wm.lat': 'Vĩ độ',
|
|
'wm.lng': 'Kinh độ',
|
|
'wm.hasExif': 'Đã đọc GPS từ ảnh.',
|
|
'wm.noExif': 'Ảnh không có GPS — nhập tay nếu muốn đóng dấu.',
|
|
|
|
'auth.loginTitle': 'Đăng nhập',
|
|
'auth.signupTitle': 'Tạo tài khoản',
|
|
'auth.email': 'Email',
|
|
'auth.password': 'Mật khẩu',
|
|
'auth.passwordHint': 'Ít nhất 8 ký tự',
|
|
'auth.noAccount': 'Chưa có tài khoản? Đăng ký',
|
|
'auth.haveAccount': 'Đã có tài khoản? Đăng nhập',
|
|
'auth.continueDemo': 'Tiếp tục dùng thử',
|
|
'auth.busy': 'Đang xử lý…',
|
|
'auth.loggedInAs': 'Đã đăng nhập: {email}',
|
|
|
|
'save.title': 'Lưu công thức',
|
|
'save.name': 'Tên công thức',
|
|
'save.recipes': 'LƯU CÔNG THỨC',
|
|
'save.saved': 'Đã lưu "{name}"',
|
|
'save.needLogin': 'Đăng nhập để lưu công thức.',
|
|
'save.photo': 'LƯU ẢNH {n}/{max}',
|
|
'save.photoNeedLogin': 'Đăng nhập để lưu ảnh.',
|
|
'save.photoSaved': 'Đã lưu ảnh {n}/{max}.',
|
|
'save.photoFull': 'Đã đủ {max} ảnh — xoá bớt trong Ảnh của tôi.',
|
|
'save.photoNoSource': 'Chưa có ảnh nào để lưu.',
|
|
'save.photoFailed': 'Không lưu được ảnh: {msg}',
|
|
'myp.newOn': 'BẢN LƯU MỚI: LÊN DẢI PHIM',
|
|
'myp.newOff': 'BẢN LƯU MỚI: RIÊNG TƯ',
|
|
'myp.thisOn': 'ẢNH NÀY: LÊN DẢI PHIM',
|
|
'myp.thisOff': 'ẢNH NÀY: RIÊNG TƯ',
|
|
'myp.empty': 'CHƯA LƯU ẢNH NÀO.',
|
|
'myp.history': 'LỊCH SỬ',
|
|
'myp.historyEmpty': 'CHƯA CÓ MỐC LƯU NÀO TRƯỚC ĐÓ.',
|
|
'myp.consent': 'Cho phép ảnh này lên landing page',
|
|
|
|
'photo.contribute': 'ĐÓNG GÓP ẢNH',
|
|
'photo.uploading': 'ĐANG TẢI…',
|
|
'photo.thanks': 'Ảnh của bạn đã vào dải phim.',
|
|
'photo.failed': 'Không tải được ảnh: {msg}',
|
|
|
|
'photos.title': 'Ảnh của tôi',
|
|
'photos.hint': 'Mỗi tài khoản giữ tối đa {max} ảnh. Ảnh lưu từ studio nằm ở đây và xuất hiện trên dải phim cộng đồng.',
|
|
'photos.count': '{n}/{max} ảnh',
|
|
'photos.add': 'THÊM ẢNH',
|
|
'photos.empty': 'Chưa có ảnh nào. Mở studio rồi bấm LƯU ẢNH, hoặc thêm ảnh ngay ở đây.',
|
|
'photos.full': 'Đã đủ {max} ảnh. Xoá bớt trước khi lưu tiếp.',
|
|
'photos.delete': 'XOÁ',
|
|
'photos.deleted': 'Đã xoá ảnh.',
|
|
'photos.failed': 'Không thực hiện được: {msg}',
|
|
'photos.noLabels': 'Chưa có nhãn dải phim',
|
|
|
|
'adm.title': 'Quản trị dải phim',
|
|
'adm.subtitle': 'Ảnh do người dùng đóng góp. Xoá một ảnh để gỡ nó khỏi trang chủ.',
|
|
'adm.upload': 'THÊM ẢNH',
|
|
'adm.slot': 'Hiển thị ở',
|
|
'adm.slotStrip': 'Dải phim cộng đồng',
|
|
'adm.slotTester': 'Live tester (lp-tester)',
|
|
'adm.slotCreator': 'Mục công thức live (lp-sec)',
|
|
'adm.slotQr': 'Thẻ QR (lp-qr)',
|
|
'adm.slotHint': 'Ba vị trí live mỗi lần vào trang sẽ bốc ngẫu nhiên một ảnh trong nhóm, nên nhiều ảnh cùng một vị trí sẽ luân phiên nhau.',
|
|
'adm.signIn': 'Đăng nhập bằng tài khoản quản trị',
|
|
'adm.notAdmin': 'Tài khoản này không có quyền quản trị.',
|
|
'adm.empty': 'Chưa có ảnh đóng góp nào.',
|
|
'adm.total': '{n} ảnh',
|
|
'adm.owner': 'Người đăng',
|
|
'adm.size': 'Dung lượng',
|
|
'adm.uploaded': 'Thời điểm',
|
|
'adm.delete': 'XOÁ',
|
|
'adm.clearAll': 'XOÁ TẤT CẢ',
|
|
'adm.clearConfirm': 'Xoá toàn bộ {n} ảnh đóng góp? Không hoàn tác được.',
|
|
'adm.removed': 'Đã xoá {n} ảnh.',
|
|
'adm.back': 'Về trang chủ',
|
|
'adm.settings': 'CÀI ĐẶT',
|
|
'adm.tabPictures': 'Ảnh',
|
|
'adm.tabUsers': 'Người dùng',
|
|
'adm.tabProfile': 'Hồ sơ',
|
|
'adm.close': 'Đóng',
|
|
'adm.userPicture': 'Ảnh',
|
|
'adm.userName': 'Tên',
|
|
'adm.userEmail': 'Email',
|
|
'adm.userRole': 'Quyền',
|
|
'adm.userPhotos': 'Số ảnh',
|
|
'adm.userJoined': 'Ngày tạo',
|
|
'adm.roleAdmin': 'Quản trị',
|
|
'adm.roleMember': 'Thành viên',
|
|
'adm.viewPhotos': 'XEM ẢNH',
|
|
'adm.noUsers': 'Chưa có tài khoản nào.',
|
|
'adm.showAll': 'TẤT CẢ',
|
|
'adm.profileHint': 'Ảnh đại diện, email và mật khẩu của tài khoản đang đăng nhập. Đổi email hoặc mật khẩu cần mật khẩu hiện tại.',
|
|
'adm.changeAvatar': 'ĐỔI ẢNH ĐẠI DIỆN',
|
|
'adm.currentPassword': 'Mật khẩu hiện tại',
|
|
'adm.newEmail': 'Email mới',
|
|
'adm.newPassword': 'Mật khẩu mới',
|
|
'adm.save': 'LƯU',
|
|
'adm.saved': 'Đã lưu hồ sơ.',
|
|
'adm.userActions': 'Thao tác',
|
|
'adm.userBlock': 'KHOÁ',
|
|
'adm.userUnblock': 'MỞ KHOÁ',
|
|
'adm.userRemove': 'GỠ',
|
|
'adm.userRestore': 'KHÔI PHỤC',
|
|
'adm.userDelete': 'XOÁ HẲN',
|
|
'adm.userBlocked': 'Đã khoá',
|
|
'adm.userRemoved': 'Đã gỡ',
|
|
'adm.userDeleteConfirm': 'Xoá vĩnh viễn tài khoản {email} cùng toàn bộ ảnh và công thức của nó? Không hoàn tác được.',
|
|
'adm.selectAll': 'CHỌN TẤT CẢ',
|
|
'adm.selectNone': 'BỎ CHỌN',
|
|
'adm.deleteSelected': 'XOÁ ĐÃ CHỌN ({n})',
|
|
'adm.userDeleteSelectedConfirm': 'Xoá vĩnh viễn {n} tài khoản đã chọn cùng toàn bộ ảnh và công thức của chúng? Không hoàn tác được.',
|
|
'adm.tabStats': 'THỐNG KÊ',
|
|
|
|
// The traffic screen. Bucket names ('/app', a control's data-key, a browser
|
|
// version) are machine values and stay as they are.
|
|
'stats.days': '{n} NGÀY',
|
|
'stats.hint': 'Lưu lượng trong khoảng đã chọn.',
|
|
'stats.views': 'LƯỢT XEM',
|
|
'stats.clicks': 'LƯỢT NHẤN',
|
|
'stats.visitors': 'KHÁCH TRUY CẬP',
|
|
'stats.timeline': 'LƯU LƯỢNG THEO NGÀY',
|
|
'stats.peak': 'đỉnh',
|
|
'stats.pages': 'TRANG ĐƯỢC XEM',
|
|
'stats.targets': 'TÍNH NĂNG ĐƯỢC NHẤN',
|
|
'stats.countries': 'QUỐC GIA',
|
|
'stats.regions': 'VÙNG MIỀN',
|
|
'stats.cities': 'THÀNH PHỐ',
|
|
'stats.browsers': 'TRÌNH DUYỆT',
|
|
'stats.systems': 'HỆ ĐIỀU HÀNH',
|
|
'stats.devices': 'THIẾT BỊ',
|
|
'stats.empty': 'Chưa có dữ liệu.',
|
|
|
|
'profile.title': 'Hồ sơ của tôi',
|
|
|
|
'err.noWebgl':
|
|
'Trình duyệt không cấp được WebGL — bản dựng này cần WebGL để xử lý ảnh (chạy CPU chậm gấp hàng nghìn lần).',
|
|
'err.decode': 'Không đọc được ảnh này. Thử JPG hoặc PNG khác.',
|
|
'err.generic': 'Có lỗi xảy ra: {msg}',
|
|
'err.offline': 'Không gọi được máy chủ.',
|
|
|
|
'meta.exported': 'Đã tải {name} ({kb}KB, 300 ppi)',
|
|
} as const;
|
|
|
|
// `vi` is `as const` so the key set is exact; `Dict` widens the values to
|
|
// string, which is what lets `en.ts` hold different text under the same keys
|
|
// while a missing or misspelled key stays a typecheck error.
|
|
export type Dict = { [K in keyof typeof vi]: string };
|
|
export type MsgKey = keyof Dict;
|