274 lines
12 KiB
TypeScript
274 lines
12 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.redo': 'LÀM LẠI',
|
|
'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.hsl': 'HSL',
|
|
'hsl.hint': 'BẤM PICK RỒI CHỌN MỘT MÀU TRÊN ẢNH',
|
|
'tab.frame': 'FRAME',
|
|
'tab.save': 'LƯU GẦN ĐÂY',
|
|
'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.recent': 'LƯU THAY ĐỔI VÀO RECIPES',
|
|
'save.recentHint': 'Lưu look đang đứng trên màn hình — sim, WB, light, FX và frame — thành một recipe riêng của tài khoản này.',
|
|
'save.recentEmpty': 'Chưa có recipe nào của riêng bạn.',
|
|
'save.delete': 'Xoá recipe này',
|
|
'name.photoTitle': 'ĐẶT TÊN ẢNH',
|
|
'name.photoLabel': 'Tên lưu trong thư mục ảnh, và là tiêu đề trên khung ảnh ở landing page.',
|
|
'name.recipeTitle': 'LƯU VÀO RECIPES',
|
|
'name.recipeLabel': 'Tên cho look này, chỉ trong tài khoản của bạn.',
|
|
'name.required': 'Đặt tên trước đã.',
|
|
'name.duplicate': 'Bạn đã có một recipe mang tên này.',
|
|
'name.save': 'LƯU',
|
|
'name.cancel': 'HUỶ',
|
|
'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.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',
|
|
'myp.remove': 'XOÁ ẢNH',
|
|
'del.title': 'XOÁ ẢNH NÀY',
|
|
'del.warning': 'Xoá ảnh này sẽ xoá tất cả history đã lưu trước đó ({n} look) và không khôi phục được. Cần tải ảnh về trước khi xoá.',
|
|
'del.download': 'TẢI VỀ TRƯỚC',
|
|
'del.confirm': 'XOÁ ẢNH',
|
|
'del.cancel': 'HUỶ',
|
|
|
|
'photo.uploading': 'ĐANG TẢI…',
|
|
|
|
'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.uploads': 'Ảnh đã tải lên',
|
|
'adm.sections': 'Mục trên landing',
|
|
'adm.albumAll': 'Tất cả ảnh',
|
|
'adm.filterName': 'Lọc theo tên người dùng',
|
|
'adm.sort': 'Sắp xếp',
|
|
'adm.sortNewest': 'Mới nhất trước',
|
|
'adm.sortRecipe': 'Công thức A→Z',
|
|
'adm.sortRating': 'Xếp hạng cao nhất',
|
|
'adm.rating': 'Xếp hạng',
|
|
'adm.ratingAll': 'Mọi mức xếp hạng',
|
|
'adm.ratingAtLeast': 'Từ {n} sao',
|
|
'adm.recipe': 'Công thức',
|
|
'adm.bigEmpty': 'Chọn một ảnh để xem.',
|
|
'adm.pickStrip': 'Dải phim',
|
|
'adm.pickTester': 'Live preset tester',
|
|
'adm.pickCreator': 'Custom recipe creator',
|
|
'adm.pickQr': 'Thẻ QR',
|
|
'adm.noPreset': 'không có look để chia sẻ',
|
|
'adm.rowEmpty': 'Chưa có gì ở đây.',
|
|
'adm.slotHint': 'Tích vào những mục ảnh sẽ xuất hiện — một ảnh có thể nằm cùng lúc ở nhiều mục, và mỗi mục bốc ngẫu nhiên một ảnh trong nhóm của nó mỗi lần ghé. Bỏ tích hết sẽ gỡ ảnh khỏi landing mà không xoá — chủ ảnh vẫn giữ nó trong MY PHOTOS. Mục QR là mục duy nhất phát công thức: thẻ của nó vẽ một mã tải chính tệp .recipe của ảnh để app trên điện thoại quét vào.',
|
|
'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.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;
|