11 Commits

Author SHA1 Message Date
Edison 93f316d8ab added for multiple clock in and out sessions on export 2025-10-17 10:22:33 +08:00
Edison 5276a7544c follow TZ 2025-10-17 09:52:53 +08:00
Edison 80d94f248e no fall back 2025-10-13 11:46:30 +08:00
Edison 356d96a61f excel download reformat on employee list. 2025-10-13 11:39:50 +08:00
Edison 411b47e897 Merge branch 'main' into edison_dev2 2025-10-09 13:39:57 +08:00
Edison 84ce4085a0 added server time 2025-10-09 11:23:35 +08:00
Edison 8e5fa5652e added current time(yellow ring) enhancement
follow server time instead of user computer local time.
2025-10-07 11:25:09 +08:00
Edison 7473cfccbc Merge branch 'main' into edison_dev2 2025-10-06 08:50:54 +08:00
Edison c7e78b910d added manual guide and also translation 2025-09-29 15:25:11 +08:00
Edison 0d23a187f0 Merge branch 'main' into edison_dev2 2025-07-24 08:38:58 +08:00
Edison b61e456e2d added english to malay translation 2025-07-02 10:19:06 +08:00
18 changed files with 2702 additions and 140 deletions
+227 -84
View File
@@ -3,10 +3,47 @@ import { Parser } from 'json2csv';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import ExcelJS from 'exceljs';
export default function(db) { export default function(db) {
const router = express.Router(); const router = express.Router();
/* === TZ helpers (no deps) === */
const _partsToObj = (parts) => parts.reduce((a, p) => (a[p.type] = p.value, a), {});
const _tzOffsetMinutes = (zone, dUtc) => {
const fmt = new Intl.DateTimeFormat('en-US', {
timeZone: zone, year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false
});
const p = _partsToObj(fmt.formatToParts(dUtc));
const asUTC = Date.UTC(+p.year, +p.month - 1, +p.day, +p.hour, +p.minute, +p.second);
return (asUTC - dUtc.getTime()) / 60000; // e.g. +480 for +08:00
};
const parseNaiveAsTZ = (s, zone) => {
if (s instanceof Date) return s;
if (typeof s === 'number') return new Date(s);
if (typeof s === 'string' && s.includes('T')) return new Date(s); // ISO with Z/offset
const [d, t = '00:00:00'] = String(s).split(' ');
const [Y, M, D] = d.split('-').map(Number);
const [h, m, sec = 0] = t.split(':').map(Number);
const guessUtc = new Date(Date.UTC(Y, M - 1, D, h, m, sec));
const off = _tzOffsetMinutes(zone, guessUtc);
return new Date(guessUtc.getTime() - off * 60000);
};
const ymdInTZ = (date, zone) =>
new Intl.DateTimeFormat('en-CA', { timeZone: zone, year: 'numeric', month: '2-digit', day: '2-digit' }).format(date);
const hmInTZ = (date, zone) =>
new Intl.DateTimeFormat('en-GB', { timeZone: zone, hour: '2-digit', minute: '2-digit', hour12: false }).format(date);
const hmsInTZ = (date, zone) =>
new Intl.DateTimeFormat('en-GB', { timeZone: zone, hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).format(date);
const ymdHmsInTZ = (date, zone) => `${ymdInTZ(date, zone)} ${hmsInTZ(date, zone)}`;
const dayNameFromYMD = (yyyyMmDd) => {
const [y, m, dd] = yyyyMmDd.split('-').map(Number);
const d = new Date(y, m - 1, dd, 12, 0, 0, 0); // noon avoids DST edges
return ['SUN','MON','TUE','WED','THU','FRI','SAT'][d.getDay()];
};
// Middleware to authenticate and authorize managers // Middleware to authenticate and authorize managers
const authenticateJWT = (req, res, next) => { const authenticateJWT = (req, res, next) => {
const authHeader = req.headers.authorization; const authHeader = req.headers.authorization;
@@ -163,7 +200,9 @@ export default function(db) {
// GET attendance records with a modified query to avoid the MySQL 5.7 bug // GET attendance records with a modified query to avoid the MySQL 5.7 bug
router.get('/attendance-records/export-raw', checkPermission('view_all'), async (req, res) => { router.get('/attendance-records/export-raw', checkPermission('view_all'), async (req, res) => {
try { try {
const { workerIds, startDate, endDate } = req.query; const { workerIds, startDate, endDate, tz } = req.query;
const TZ = tz || process.env.EXPORT_TZ || 'Asia/Kuala_Lumpur';
if (!startDate || !endDate) { if (!startDate || !endDate) {
return res.status(400).json({ message: 'Start date and end date are required.' }); return res.status(400).json({ message: 'Start date and end date are required.' });
} }
@@ -189,9 +228,19 @@ export default function(db) {
const [rows] = await db.execute(query, params); const [rows] = await db.execute(query, params);
const json2csvParser = new Parser({ fields: ['username', 'full_name', 'event_type', 'timestamp', 'qr_code_name', 'notes'] }); // Format timestamp per TZ as "YYYY-MM-DD HH:mm:ss"
const csv = json2csvParser.parse(rows); const shaped = rows.map(r => ({
res.header('Content-Type', 'text/csv').attachment(`raw_attendance_${startDate}_to_${endDate}.csv`).send(csv); username: r.username,
full_name: r.full_name,
event_type: r.event_type,
timestamp: ymdHmsInTZ(parseNaiveAsTZ(r.timestamp, TZ), TZ),
qr_code_name: r.qr_code_name,
notes: r.notes
}));
const json2csvParser = new Parser({ fields: ['username', 'full_name', 'event_type', 'timestamp', 'qr_code_name', 'notes'] });
const csv = json2csvParser.parse(shaped);
res.header('Content-Type', 'text/csv').attachment(`raw_attendance_${startDate}_to_${endDate}.csv`).send(csv);
} catch (error) { } catch (error) {
console.error('Raw attendance export error:', error); console.error('Raw attendance export error:', error);
@@ -234,88 +283,182 @@ export default function(db) {
} }
}) })
router.get('/attendance-records/export', checkPermission('view_all'), async (req, res) => { router.get('/attendance-records/export', checkPermission('view_all'), async (req, res) => {
try { try {
const { workerIds, startDate, endDate } = req.query; const { workerIds, startDate, endDate, tz } = req.query;
if (!startDate || !endDate) { const TZ = tz || process.env.EXPORT_TZ || 'Asia/Kuala_Lumpur';
return res.status(400).json({ message: 'Start date and end date are required.' });
}
let workerIdClause = ''; if (!startDate || !endDate) {
const params = [startDate, `${endDate} 23:59:59`]; return res.status(400).json({ message: 'Start date and end date are required.' });
if (workerIds) {
const idsArray = workerIds.split(',').map(Number).filter(id => !isNaN(id));
if (idsArray.length > 0) {
workerIdClause = `AND cr.worker_id IN (${idsArray.join(',')})`;
}
}
const query = `
SELECT cr.worker_id, w.username, w.full_name, cr.event_type, cr.timestamp
FROM clock_records cr
JOIN workers w ON cr.worker_id = w.id
WHERE cr.timestamp BETWEEN ? AND ? ${workerIdClause}
ORDER BY cr.worker_id, cr.timestamp ASC
`;
const [rows] = await db.execute(query, params);
const workHoursByWorkerAndDay = {};
rows.forEach(row => {
const day = new Date(row.timestamp).toISOString().split('T')[0];
if (!workHoursByWorkerAndDay[row.worker_id]) {
workHoursByWorkerAndDay[row.worker_id] = {
username: row.username,
full_name: row.full_name,
days: {}
};
}
if (!workHoursByWorkerAndDay[row.worker_id].days[day]) {
workHoursByWorkerAndDay[row.worker_id].days[day] = [];
}
workHoursByWorkerAndDay[row.worker_id].days[day].push({
type: row.event_type,
time: new Date(row.timestamp)
});
});
const csvData = [];
for (const workerId in workHoursByWorkerAndDay) {
const workerData = workHoursByWorkerAndDay[workerId];
for (const day in workerData.days) {
const events = workerData.days[day];
let dailyTotalSeconds = 0;
let lastClockIn = null;
events.forEach(event => {
if (event.type === 'clock_in') {
lastClockIn = event.time;
} else if (event.type === 'clock_out' && lastClockIn) {
dailyTotalSeconds += (event.time - lastClockIn) / 1000;
lastClockIn = null;
}
});
csvData.push({
username: workerData.username,
full_name: workerData.full_name,
date: day,
work_hours: (dailyTotalSeconds / 3600).toFixed(2)
});
}
}
const json2csvParser = new Parser({ fields: ['username', 'full_name', 'date', 'work_hours'] });
const csv = json2csvParser.parse(csvData);
res.header('Content-Type', 'text/csv').attachment(`work_hours_${startDate}_to_${endDate}.csv`).send(csv);
} catch (error) {
console.error('Work hours export error:', error);
res.status(500).json({ message: 'Database error exporting work hours.', details: error.message });
} }
});
const wantXlsx = String(req.query.format || 'csv').toLowerCase() === 'xlsx';
let workerIdClause = '';
const params = [startDate, `${endDate} 23:59:59`];
if (workerIds) {
const idsArray = workerIds.split(',').map(Number).filter(id => !isNaN(id));
if (idsArray.length > 0) {
workerIdClause = `AND cr.worker_id IN (${idsArray.join(',')})`;
}
}
const query = `
SELECT
cr.worker_id,
w.username,
w.full_name,
w.department,
cr.event_type,
cr.timestamp,
COALESCE(qc.name, 'Manual Entry') AS qr_code_name
FROM clock_records cr
JOIN workers w ON cr.worker_id = w.id
LEFT JOIN qr_codes qc ON cr.qr_code_id = qc.id
WHERE cr.timestamp BETWEEN ? AND ? ${workerIdClause}
AND cr.event_type IN ('clock_in','clock_out')
ORDER BY cr.worker_id, cr.timestamp ASC
`;
const [rows] = await db.execute(query, params);
// ---- Group events by worker/day ----
const workByDay = {};
rows.forEach(row => {
const ts = parseNaiveAsTZ(row.timestamp, TZ);
const day = ymdInTZ(ts, TZ);
if (!workByDay[row.worker_id]) {
workByDay[row.worker_id] = {
username: row.username,
full_name: row.full_name,
department: row.department || '',
days: {}
};
}
if (!workByDay[row.worker_id].days[day]) {
workByDay[row.worker_id].days[day] = [];
}
workByDay[row.worker_id].days[day].push({
type: row.event_type,
time: ts,
qr_code_name: row.qr_code_name
});
});
// ---- Build rows: one per successful [clock_in, clock_out] session ----
const csvData = [];
const byWorkerForXlsx = new Map(); // key = "username||full_name||department" → daily rows
for (const workerId in workByDay) {
const w = workByDay[workerId];
const perWorkerRows = [];
for (const day of Object.keys(w.days).sort()) {
// events for this day in ascending time
const events = w.days[day].slice().sort((a, b) => a.time - b.time);
let open = null;
let openQr = 'Manual Entry';
for (const e of events) {
if (e.type === 'clock_in' && open == null) {
open = e.time;
openQr = e.qr_code_name || 'Manual Entry';
} else if (e.type === 'clock_out' && open != null) {
const start = open;
const end = e.time;
const dailyRow = {
username: w.username,
full_name: w.full_name,
date: day,
day: dayNameFromYMD(day),
clock_in: hmInTZ(start, TZ),
clock_out: hmInTZ(end, TZ),
work_hours: ((end - start) / 3600000).toFixed(2),
qr_code_name: openQr
};
csvData.push(dailyRow);
perWorkerRows.push(dailyRow);
// close the session
open = null;
openQr = 'Manual Entry';
}
}
}
byWorkerForXlsx.set(`${w.username}||${w.full_name}||${w.department}`, perWorkerRows);
}
// ===== XLSX branch: grouped header + per-day summary columns =====
if (wantXlsx) {
const wb = new ExcelJS.Workbook();
const ws = wb.addWorksheet('Attendance');
ws.columns = [
{ header: 'Date', key: 'date', width: 12 },
{ header: 'Day', key: 'day', width: 8 },
{ header: 'Clock In', key: 'clock_in', width: 10 },
{ header: 'Clock Out', key: 'clock_out', width: 10 },
{ header: 'Work Hours', key: 'work_hours', width: 12 },
{ header: 'QR Code', key: 'qr_code_name', width: 24 },
];
for (const [key, rowsForWorker] of byWorkerForXlsx.entries()) {
const [username, full_name, dept] = key.split('||');
if (ws.lastRow) ws.addRow([]);
// Bold merged group header: "username full_name Dept: X"
const titleRowIdx = (ws.lastRow ? ws.lastRow.number : 0) + 1;
ws.mergeCells(`A${titleRowIdx}:F${titleRowIdx}`);
const titleCell = ws.getCell(`A${titleRowIdx}`);
titleCell.value = dept ? `${username} ${full_name} Dept: ${dept}` : `${username} ${full_name}`;
titleCell.font = { bold: true, size: 12 };
titleCell.alignment = { horizontal: 'left', vertical: 'middle' };
// Header row under the group
const hdr = ws.addRow({
date: 'Date',
day: 'Day',
clock_in: 'Clock In',
clock_out: 'Clock Out',
work_hours: 'Work Hours',
qr_code_name: 'QR Code',
});
hdr.font = { bold: true };
// Detail rows (one per day)
for (const r of rowsForWorker) {
ws.addRow(r);
}
}
ws.eachRow(row => { row.alignment = { vertical: 'middle' }; });
const buf = await wb.xlsx.writeBuffer();
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', `attachment; filename="work_hours_${startDate}_to_${endDate}.xlsx"`);
return res.send(Buffer.from(buf));
}
// ===== CSV fallback: one row per day; include identity columns =====
const json2csvParser = new Parser({
fields: ['username','full_name','date','day','clock_in','clock_out','work_hours','qr_code_name']
});
const csv = json2csvParser.parse(csvData);
res
.header('Content-Type', 'text/csv')
.attachment(`work_hours_${startDate}_to_${endDate}.csv`)
.send(csv);
} catch (error) {
console.error('Work hours export error:', error);
res.status(500).json({ message: 'Database error exporting work hours.', details: error.message });
}
});
router.get('/attendance-records', checkPermission('view_all'), async (req, res) => { router.get('/attendance-records', checkPermission('view_all'), async (req, res) => {
try { try {
+16
View File
@@ -59,6 +59,22 @@ async function startServer() {
app.use(cors(corsOptions)); app.use(cors(corsOptions));
app.use(express.json()); app.use(express.json());
// --- Public server time endpoints (no auth, no cache) ---
const timeHandler = (req, res) => {
const now = new Date();
const ymdKL = new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Kuala_Lumpur',
year: 'numeric', month: '2-digit', day: '2-digit'
}).format(now); // "YYYY-MM-DD"
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
res.set('Pragma', 'no-cache');
res.set('Expires', '0');
res.json({ nowIso: now.toISOString(), tz: 'Asia/Kuala_Lumpur', ymdKL });
};
app.get('/time', timeHandler); // public path
app.get('/api/time', timeHandler); // also under /api
app.use('/api/managers', managerRoutes(db)); app.use('/api/managers', managerRoutes(db));
app.use('/api', workerRoutes(db)); app.use('/api', workerRoutes(db));
+896 -6
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -19,6 +19,7 @@
"body-parser": "^2.2.0", "body-parser": "^2.2.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.6.1", "dotenv": "^16.6.1",
"exceljs": "^4.4.0",
"express": "^5.1.0", "express": "^5.1.0",
"html5-qrcode": "^2.3.8", "html5-qrcode": "^2.3.8",
"json2csv": "^6.0.0-alpha.2", "json2csv": "^6.0.0-alpha.2",
+44 -5
View File
@@ -64,7 +64,7 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed, onUnmounted } from 'vue';
import { apiFetch } from '@/api.js'; import { apiFetch } from '@/api.js';
import { useToast } from '@/composables/useToast'; import { useToast } from '@/composables/useToast';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -73,7 +73,46 @@ const { t: $t } = useI18n();
const toast = useToast(); const toast = useToast();
const viewDate = ref(new Date()); const viewDate = ref(new Date());
const todayStr = new Date().toISOString().slice(0, 10); // Server-driven KL date for the yellow ring (updates every 60s)
const todayStr = ref(null);
const TZ = 'Asia/Kuala_Lumpur';
// Helper: format YYYY-MM-DD in a given TZ
const ymdInTZ = (tz, d = new Date()) =>
new Intl.DateTimeFormat('en-CA', {
timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit'
}).format(d);
// Pull today from server; try /api/time then /time; fallback to client KL
async function getServerDate() {
const parse = (data) => {
if (typeof data?.ymdKL === 'string') return data.ymdKL;
if (data?.nowIso) return ymdInTZ(TZ, new Date(data.nowIso));
return null;
};
for (const path of ['/api/time', '/time']) {
try {
const d = await apiFetch(`${path}?_t=${Date.now()}`);
const y = parse(d);
if (y) return y;
} catch (_err) {
continue; // try next endpoint
}
}
console.warn('Server time unavailable; using client KL time.');
return ymdInTZ(TZ, new Date());
}
let _intervalId;
onMounted(async () => {
const update = async () => { todayStr.value = await getServerDate(); };
await update();
_intervalId = setInterval(update, 60_000);
});
onUnmounted(() => { if (_intervalId) clearInterval(_intervalId); });
const originalEnabledDates = ref(new Set()); const originalEnabledDates = ref(new Set());
const datesToEnable = ref(new Set()); const datesToEnable = ref(new Set());
@@ -129,9 +168,9 @@ const getDayClasses = (day) => {
classes.push('bg-white', 'dark:bg-gray-800', 'hover:bg-gray-100', 'dark:hover:bg-gray-700'); classes.push('bg-white', 'dark:bg-gray-800', 'hover:bg-gray-100', 'dark:hover:bg-gray-700');
} }
if (dateStr === todayStr) { if (todayStr.value && dateStr === todayStr.value) {
classes.push('ring-2', 'ring-yellow-400', 'dark:ring-yellow-500'); classes.push('ring-2', 'ring-yellow-400', 'dark:ring-yellow-500');
} }
return classes; return classes;
}; };
+7 -7
View File
@@ -516,22 +516,22 @@ const toggleSelectAll = (event) => {
const exportWorkHours = async () => { const exportWorkHours = async () => {
const toast = useToast(); const toast = useToast();
exportLoading.value = true; exportLoading.value = true;
toast.showToast($t('exportingRecords'), 'info'); toast.showToast($t('exportingRecords'), 'info');
const { startDate, endDate } = exportFilters.value; const { startDate, endDate } = exportFilters.value;
let workerIds = selectedWorkerIds.value.join(','); let workerIds = selectedWorkerIds.value.join(',');
try { try {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/managers/attendance-records/export?startDate=${startDate}&endDate=${endDate}&workerIds=${workerIds}`, { const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/managers/attendance-records/export?format=xlsx&startDate=${startDate}&endDate=${endDate}&workerIds=${workerIds}`, {
headers: { headers: {
'Authorization': `Bearer ${sessionStorage.getItem('token')}` 'Authorization': `Bearer ${sessionStorage.getItem('token')}`
} }
}); });
if (!response.ok) throw new Error('Network response was not ok.'); if (!response.ok) throw new Error('Network response was not ok.');
const blob = await response.blob(); const blob = await response.blob();
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
a.download = `work_hours_${startDate}_to_${endDate}.csv`; a.download = `work_hours_${startDate}_to_${endDate}.xlsx`;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
a.remove(); a.remove();
@@ -539,7 +539,7 @@ const exportWorkHours = async () => {
} catch (_err) { } catch (_err) {
toast.showToast($t('exportRecordsFailed'), 'error'); toast.showToast($t('exportRecordsFailed'), 'error');
} finally { } finally {
exportLoading.value = false; exportLoading.value = false;
} }
}; };
+10 -2
View File
@@ -3,15 +3,23 @@ console.log("[DEBUG] i18n.js loaded!"); // very top
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import en from './locales/en.json'; import en from './locales/en.json';
import ms from './locales/ms.json'; import ms from './locales/ms.json';
import tm from './locales/tm.json';
import bd from './locales/bd.json';
import my from './locales/my.json';
import np from './locales/np.json';
console.log("[DEBUG] en.json:", en); console.log("[DEBUG] en.json:", en);
console.log("[DEBUG] ms.json:", ms); console.log("[DEBUG] ms.json:", ms);
console.log("[DEBUG] tm.json:", tm);
console.log("[DEBUG] bd.json:", bd);
console.log("[DEBUG] my.json:", my);
console.log("[DEBUG] np.json:", np);
const i18n = createI18n({ const i18n = createI18n({
legacy: false, legacy: false,
locale: 'en', // Default to English locale: 'en', // keep original; App.vue will override from localStorage
fallbackLocale: 'en', fallbackLocale: 'en',
messages: { en, ms } messages: { en, ms, tm, bd, my, np } // register all locales here
}); });
console.log("[DEBUG] i18n instance created:", i18n); console.log("[DEBUG] i18n instance created:", i18n);
+315
View File
@@ -0,0 +1,315 @@
{
"appTitle": "উপস্থিতি ব্যবস্থা",
"logout": "লগ আউট",
"login": "লগ ইন",
"username": "ইউজারনেম",
"password": "পাসওয়ার্ড",
"loggingIn": "লগ ইন করা হচ্ছে...",
"language": "ভাষা",
"darkMode": "ডার্ক মোড",
"toggleDarkMode": "হালকা এবং অন্ধকার থিমের মধ্যে পরিবর্তন করুন",
"failedConnection": "সার্ভারের সাথে সংযোগ করতে পারেনি।",
"invalidToken": "সার্ভার থেকে অবৈধ টোকেন পাওয়া গেছে।",
"invalidCredentials": "ভুল ইউজারনেম বা পাসওয়ার্ড।",
"english": "ইংরেজি",
"malay": "মালয়",
"tamil": "তামিল",
"bengali": "বাংলা",
"burmese": "বর্মী",
"nepali": "নেপালি",
"yourStatus": "স্ট্যাটাস",
"clockedIn": "উপস্থিতি রেকর্ড হয়েছে",
"clockedOut": "প্রস্থান রেকর্ড হয়েছে",
"clockIn": "উপস্থিতি রেকর্ড",
"clockOut": "প্রস্থান রেকর্ড",
"clock_in": "উপস্থিতি রেকর্ড",
"clock_out": "প্রস্থান রেকর্ড",
"scanToClock": "{action} রেকর্ড করতে স্ক্যান করুন",
"in": "উপস্থিতি",
"out": "প্রস্থান",
"cancel": "বাতিল",
"viewMyClockHistory": "আমার উপস্থিতির ইতিহাস দেখুন",
"changeMyPassword": "আমার পাসওয়ার্ড পরিবর্তন করুন",
"myClockHistory": "আমার উপস্থিতির ইতিহাস",
"backToDashboard": "ড্যাশবোর্ডে ফিরে যান",
"noClockHistory": "আপনার কোনো উপস্থিতির ইতিহাস নেই।",
"clockHistoryFetchFail": "উপস্থিতির ইতিহাস পেতে পারেনি:",
"viewClockHistory": "আমার উপস্থিতির ইতিহাস দেখুন",
"changePassword": "আমার পাসওয়ার্ড পরিবর্তন করুন",
"invalidCurrentPassword": "বর্তমান পাসওয়ার্ড ভুল।",
"successClockIn": "সফলভাবে উপস্থিতি রেকর্ড হয়েছে।",
"successClockOut": "সফলভাবে প্রস্থান রেকর্ড হয়েছে।",
"qrFail": "QR কোড খুঁজে পাওয়া যায়নি। অনুগ্রহ করে আবার চেষ্টা করুন।",
"geoFail": "আপনার অবস্থান খুঁজে পাওয়া যায়নি: {message}। অনুগ্রহ করে লোকেশন সার্ভিস চালু করুন।",
"successClock": "{location} এ সফলভাবে রেকর্ড হয়েছে।",
"changePasswordTitle": "পাসওয়ার্ড পরিবর্তন করুন",
"currentPassword": "বর্তমান পাসওয়ার্ড",
"newPassword": "নতুন পাসওয়ার্ড",
"confirmNewPassword": "নতুন পাসওয়ার্ড নিশ্চিত করুন",
"updating": "আপডেট করা হচ্ছে...",
"tabPersonnel": "কর্মী",
"tabAttendance": "উপস্থিতি",
"tabQrCodes": "QR কোড",
"uploadQrImage": "QR ছবি আপলোড করুন",
"couldNotLoadWorkerInfo": "কর্মীর তথ্য লোড করতে পারেনি",
"couldNotVerifyStatus": "সার্ভার থেকে বর্তমান স্ট্যাটাস যাচাই করতে পারেনি",
"successfullyClocked": "সফলভাবে {action} রেকর্ড হয়েছে",
"site": "স্থানে",
"errorOccurred": "একটি ত্রুটি ঘটেছে",
"unableToStartCamera": "ক্যামেরা চালু করতে পারেনি।",
"tryAgain": "আবার চেষ্টা করুন",
"qrDetectedGettingLocation": "QR কোড পাওয়া গেছে। অবস্থান খুঁজে বের করা হচ্ছে...",
"geolocationNotSupported": "আপনার ব্রাউজার জিওলোকেশন সাপোর্ট করে না।",
"unableToRetrieveLocation": "আপনার অবস্থান খুঁজে পাওয়া যায়নি: {message}। অনুগ্রহ করে লোকেশন সার্ভিস চালু করুন।",
"qrNotDetectedTryAgain": "QR কোড খুঁজে পাওয়া যায়নি। অনুগ্রহ করে আবার চেষ্টা করুন।",
"updatePassword": "পাসওয়ার্ড আপডেট করুন",
"passwordsNoMatch": "নতুন পাসওয়ার্ড মিলছে না।",
"passwordTooShort": "নতুন পাসওয়ার্ড কমপক্ষে ৬ অক্ষরের হতে হবে।",
"passwordUpdated": "পাসওয়ার্ড সফলভাবে আপডেট হয়েছে! এখন আপনি নতুন পাসওয়ার্ড দিয়ে লগ ইন করতে পারবেন।",
"passwordUpdateError": "পাসওয়ার্ড আপডেট করার সময় একটি ত্রুটি হয়েছে।",
"attendanceLogFor": "উপস্থিতির রেকর্ড -",
"addManualClockOut": "ম্যানুয়াল প্রস্থান রেকর্ড যোগ করুন",
"manualClockOutInstruction": "কর্মী প্রস্থান রেকর্ড করতে ভুলে গেলে এই ফর্মটি ব্যবহার করুন। শেষ ইভেন্ট অবশ্যই উপস্থিতি রেকর্ড হতে হবে।",
"clockOutTime": "প্রস্থানের সময়",
"reason": "কারণ (যেমন, \"প্রস্থান রেকর্ড করতে ভুলে গেছেন\")",
"enterBriefNote": "সংক্ষিপ্ত নোট লিখুন",
"addRecord": "রেকর্ড যোগ করুন",
"startDate": "শুরুর তারিখ",
"endDate": "শেষ তারিখ",
"filterRecords": "রেকর্ড ফিল্টার করুন",
"event": "ইভেন্ট",
"timestamp": "সময়ের স্ট্যাম্প",
"locationName": "স্থানের নাম",
"coordinates": "স্থানাঙ্ক",
"notes": "নোট",
"noRecordsFound": "এই সময়ের জন্য কোনো রেকর্ড পাওয়া যায়নি।",
"showOnMap": "মানচিত্রে দেখুন",
"nA": "নেই",
"pleaseSelectTimestamp": "অনুগ্রহ করে প্রস্থানের জন্য সময়ের স্ট্যাম্প নির্বাচন করুন।",
"pleaseProvideReason": "অনুগ্রহ করে ম্যানুয়াল এন্ট্রির জন্য কারণ/নোট প্রদান করুন।",
"manualClockOutSuccess": "ম্যানুয়াল প্রস্থান সফলভাবে রেকর্ড হয়েছে!",
"manualClockOutError": "একটি ত্রুটি ঘটেছে: {message}",
"selectWorkers": "১. কর্মী নির্বাচন করুন",
"searchWorkerPlaceholder": "একজন কর্মী খুঁজুন...",
"selectAll": "সব নির্বাচন করুন",
"addWorkersByTag": "একটি ট্যাগ থেকে সব কর্মী যোগ করুন",
"chooseTag": "-- একটি ট্যাগ বেছে নিন --",
"addByTag": "ট্যাগ দ্বারা যোগ করুন",
"selectedForReport": "রিপোর্টের জন্য নির্বাচিত ({count})",
"allWorkersSelected": "সব কর্মী ({count}) নির্বাচিত",
"noWorkersSelected": "কোনো কর্মী নির্বাচিত নয়।",
"reportSettings": "২. রিপোর্ট সেটিংস",
"setting": "সেটিং",
"monthlySalary": "মাসিক বেতন (RM)",
"salaryAppliedNote": "নির্বাচিত সব কর্মীর জন্য প্রযোজ্য।",
"salaryPlaceholder": "যেমন, ৩০০০",
"otFactors": "ওভারটাইম ফ্যাক্টর",
"weekendFactor": "সাপ্তাহিক ছুটির ফ্যাক্টর",
"holidayFactor": "ছুটির দিনের ফ্যাক্টর",
"selectPublicHolidays": "সরকারি ছুটির দিন নির্বাচন করুন",
"generateReport": "উপস্থিতি ও ওভারটাইম রিপোর্ট তৈরি করুন",
"overtimePaySummary": "ওভারটাইম বেতনের সারসংক্ষেপ",
"exportOtSummary": "ওভারটাইম সারসংক্ষেপ রপ্তানি করুন (CSV)",
"worker": "কর্মী",
"totalHoursWorked": "মোট কাজের ঘন্টা",
"totalOtPay": "মোট ওভারটাইম বেতন (RM)",
"rawAttendanceData": "কাঁচা উপস্থিতির তথ্য",
"loadingReport": "রিপোর্ট লোড করা হচ্ছে...",
"tagLoadError": "নির্বাচিত ট্যাগের জন্য কর্মীদের লোড করতে পারেনি।",
"generateReportError": "অনুগ্রহ করে কর্মী নির্বাচন করুন, সঠিক তারিখের সীমা নির্ধারণ করুন এবং বেতন লিখুন।",
"reportGenerationError": "রিপোর্ট তৈরি করার সময় একটি ত্রুটি ঘটেছে।",
"addNewUser": "নতুন ইউজার যোগ করুন",
"fullName": "পূর্ণ নাম",
"egJohnSmith": "যেমন John Smith",
"egJsmith": "যেমন jsmith",
"eg123456": "যেমন ১২৩৪৫৬",
"asManager": "ম্যানেজার হিসেবে",
"adding": "যোগ করা হচ্ছে...",
"addUser": "ইউজার যোগ করুন",
"manageTags": "ট্যাগ পরিচালনা করুন",
"createNewTag": "নতুন ট্যাগ তৈরি করুন",
"egTeam": "যেমন টিম",
"createTag": "ট্যাগ তৈরি করুন",
"tags": "ট্যাগ",
"workerRoster": "কর্মীদের তালিকা",
"searchByNameOrUsername": "নাম বা ইউজারনেম দিয়ে খুঁজুন",
"filterByTag": "ট্যাগ দিয়ে ফিল্টার করুন",
"clearFilter": "ফিল্টার পরিষ্কার করুন",
"dateJoined": "যোগদানের তারিখ",
"actions": "কার্যক্রম",
"editTags": "ট্যাগ সম্পাদনা করুন",
"viewRecords": "রেকর্ড দেখুন",
"delete": "মুছে ফেলুন",
"loadingWorkers": "কর্মীরা লোড হচ্ছে...",
"noWorkersFound": "কোনো কর্মী পাওয়া যায়নি।",
"previous": "পূর্ববর্তী",
"next": "পরবর্তী",
"pageOf": "পৃষ্ঠা {current} এর {total}",
"noTagsAvailable": "কোনো ট্যাগ উপলব্ধ নেই।",
"done": "সম্পন্ন",
"bulkEditTags": "একাধিক ট্যাগ সম্পাদনা করুন",
"clearSelection": "নির্বাচন পরিষ্কার করুন",
"forUser": "ইউজারের জন্য",
"savePassword": "পাসওয়ার্ড সেভ করুন",
"saving": "সেভ করা হচ্ছে...",
"failedToUpdateTags": "ট্যাগ আপডেট করতে পারেনি। অনুগ্রহ করে আবার চেষ্টা করুন।",
"tagDeleted": "ট্যাগ সফলভাবে মুছে ফেলা হয়েছে।",
"failedToFetchWorkers": "কর্মীদের তথ্য পেতে পারেনি।",
"failedToLoadPageData": "পেজের তথ্য লোড করতে পারেনি।",
"errorAddingUser": "ইউজার যোগ করার সময় একটি ত্রুটি ঘটেছে।",
"failedToDeleteWorker": "কর্মী মুছে ফেলতে পারেনি।",
"areYouSureDeleteWorker": "আপনি কি নিশ্চিত এই কর্মীর অ্যাকাউন্ট মুছে ফেলতে চান?",
"areYouSureDeleteTag": "আপনি কি নিশ্চিত এই ট্যাগটি মুছে ফেলতে চান? এটি সব কর্মী থেকে সরিয়ে দেওয়া হবে।",
"failedToDeleteTag": "ট্যাগ মুছে ফেলতে পারেনি।",
"passwordsDoNotMatch": "পাসওয়ার্ড মিলছে না।",
"createQrCode": "নতুন QR কোড তৈরি করুন",
"qrCodeName": "QR কোডের নাম",
"qrNamePlaceholder": "যেমন, 'পশ্চিম গেট প্রবেশদ্বার'",
"create": "তৈরি করুন",
"newCodeCreated": "নতুন কোড তৈরি হয়েছে!",
"saveQrInstruction": "এই ছবিটি সেভ করুন বা নিচের ID ব্যবহার করুন। এটি রিফ্রেশে অদৃশ্য হয়ে যাবে।",
"id": "আইডি",
"existingQrCodes": "বিদ্যমান QR কোড",
"name": "নাম",
"status": "স্ট্যাটাস",
"active": "সক্রিয়",
"inactive": "নিষ্ক্রিয়",
"deactivate": "নিষ্ক্রিয় করুন",
"activate": "সক্রিয় করুন",
"download": "ডাউনলোড",
"noQrCodesFound": "কোনো QR কোড পাওয়া যায়নি। উপরে একটি তৈরি করুন!",
"deleteQrConfirm": "আপনি কি নিশ্চিত এই QR কোডটি মুছে ফেলতে চান? এটি পূর্বাবস্থায় ফেরানো যাবে না।",
"qrDownloadError": "দুঃখিত, QR কোড ডাউনলোড করা যায়নি।",
"rememberMe": "অটো লগইনের জন্য আমাকে মনে রাখুন",
"deviceNotAuthorized": "এই ডিভাইসটি আপনার অ্যাকাউন্টের জন্য অনুমোদিত নয়। অনুগ্রহ করে আপনার অ্যাডমিনিস্ট্রেটরের সাথে যোগাযোগ করুন।",
"locationTrackingActive": "লোকেশন ট্র্যাকিং পেছনে চালু আছে",
"securityCheckInProgress": "নিরাপত্তা যাচাই চলছে...",
"securityCheckComplete": "নিরাপত্তা যাচাই সফলভাবে সম্পন্ন",
"highSecurityRisk": "উচ্চ নিরাপত্তা ঝুঁকি শনাক্ত। অনুগ্রহ করে আপনার অ্যাডমিনিস্ট্রেটরের সাথে যোগাযোগ করুন।",
"deviceRegistered": "ডিভাইস সফলভাবে নিবন্ধিত হয়েছে",
"autoLoginEnabled": "এই ডিভাইসের জন্য অটো লগইন চালু করা হয়েছে",
"backgroundLocationEnabled": "পেছনের লোকেশন ট্র্যাকিং চালু করা হয়েছে",
"permissionsRequired": "উপস্থিতি ট্র্যাকিংয়ের জন্য লোকেশন অনুমতি প্রয়োজন",
"batteryOptimizationWarning": "অবিরাম লোকেশন ট্র্যাকিং নিশ্চিত করতে এই অ্যাপের জন্য ব্যাটারি অপটিমাইজেশন বন্ধ করুন",
"gpsSpooferDetected": "GPS স্পুফিং অ্যাপ্লিকেশন শনাক্ত হয়েছে। এটি উপস্থিতির নির্ভুলতা প্রভাবিত করতে পারে।",
"mockLocationEnabled": "মক লোকেশন চালু আছে। নির্ভুল উপস্থিতি ট্র্যাকিংয়ের জন্য এটি বন্ধ করুন।",
"deviceSecurityWarning": "ডিভাইস নিরাপত্তা সতর্কতা: সন্দেহজনক অ্যাপ্লিকেশন শনাক্ত",
"locationUpdateFailed": "লোকেশন আপডেট করতে পারেনি। স্বয়ংক্রিয়ভাবে আবার চেষ্টা করা হবে।",
"servicesInitializing": "নেটিভ সার্ভিস চালু করা হচ্ছে...",
"servicesReady": "সব সার্ভিস প্রস্তুত",
"autoLoginFailed": "অটো লগইন ব্যর্থ। অনুগ্রহ করে ম্যানুয়ালি লগ ইন করুন।",
"deviceValidationFailed": "ডিভাইস ভেরিফিকেশন ব্যর্থ। অনুগ্রহ করে সাপোর্টের সাথে যোগাযোগ করুন।",
"deviceMismatch": "এই ডিভাইসটি আপনার অ্যাকাউন্টের জন্য অনুমোদিত নয়।",
"deviceRegistrationFailed": "ডিভাইস নিবন্ধন ব্যর্থ। আবার চেষ্টা করুন।",
"deviceRequired": "কর্মী লগইনের জন্য ডিভাইস নিবন্ধন প্রয়োজন।",
"servicesStatus": "সার্ভিসের স্ট্যাটাস",
"overallStatus": "সামগ্রিক স্ট্যাটাস",
"locationTracking": "লোকেশন ট্র্যাকিং",
"deviceRegistration": "ডিভাইস নিবন্ধন",
"securityStatus": "নিরাপত্তার স্ট্যাটাস",
"lastLocationUpdate": "শেষ লোকেশন আপডেট",
"deviceId": "ডিভাইস আইডি",
"start": "শুরু করুন",
"check": "যাচাই করুন",
"checking": "যাচাই করা হচ্ছে...",
"refresh": "রিফ্রেশ করুন",
"refreshing": "রিফ্রেশ করা হচ্ছে...",
"notInitialized": "চালু করা হয়নি",
"ready": "প্রস্তুত",
"webOnly": "শুধু ওয়েব",
"registered": "নিবন্ধিত",
"pending": "অপেক্ষমান",
"notChecked": "যাচাই করা হয়নি",
"outdated": "পুরোনো",
"current": "বর্তমান",
"never": "কখনো না",
"justNow": "এইমাত্র",
"minutesAgo": "{minutes} মিনিট আগে",
"hoursAgo": "{hours} ঘন্টা আগে",
"daysAgo": "{days} দিন আগে",
"failedToRefreshStatus": "স্ট্যাটাস রিফ্রেশ করতে পারেনি",
"locationTrackingStarted": "লোকেশন ট্র্যাকিং সফলভাবে শুরু হয়েছে",
"failedToStartLocationTracking": "লোকেশন ট্র্যাকিং শুরু করতে পারেনি",
"securityCheckFailed": "নিরাপত্তা যাচাই ব্যর্থ",
"personal": "ব্যক্তিগত",
"clockHistory": "উপস্থিতির ইতিহাস",
"openCamera": "ক্যামেরা খুলুন",
"scanQRCode": "QR কোড স্ক্যান করুন",
"services": "সার্ভিস",
"systemServicesStatus": "সিস্টেম সার্ভিস এবং নিরাপত্তার স্ট্যাটাস",
"updateYourPassword": "আপনার অ্যাকাউন্টের পাসওয়ার্ড আপডেট করুন",
"signOutOfAccount": "আপনার অ্যাকাউন্ট থেকে সাইন আউট করুন",
"workLocationTracking": "কর্মক্ষেত্রের লোকেশন ট্র্যাকিং",
"locationTrackingForAttendance": "কাজের উপস্থিতির জন্য লোকেশন ট্র্যাকিং সক্রিয়",
"monitoringLocation": "কাজের উপস্থিতির জন্য লোকেশন নিরীক্ষণ করা হচ্ছে",
"manualGuide": "ম্যানুয়াল গাইড ",
"viewUserManual": "নির্দেশাবলী এবং FAQs পড়ুন",
"manual": {
"android": {
"heading": "Android",
"faqs": [
{
"id": "android-location",
"title": "Location কীভাবে খুলবেন (Android)",
"steps": [
"আপনার ফোনে <strong>Settings</strong> খুলুন।",
"<strong>Location</strong> এ যান <span class=\"text-sm text-gray-500\">(কিছু ফোনে <em>Security &amp; privacy</em> এর অধীনে)</span>।",
"<strong>Use location</strong> ON করুন।",
"<strong>App permissions</strong> খুলুন → <strong>Attendance System</strong> খুঁজুন → <strong>Allow while using the app</strong> সেট করুন।",
"উপলব্ধ থাকলে <strong>Precise location</strong> সক্রিয় করুন।",
"App এ ফিরে যান এবং আবার clock-in করার চেষ্টা করুন।"
],
"note": "ব্র্যান্ড অনুযায়ী নাম ভিন্ন: Samsung → Settings → Location → App permissions. Xiaomi → Settings → Location → Location services।"
},
{
"id": "android-camera",
"title": "Camera permission সক্রিয় করুন (Android)",
"steps": [
"<strong>Settings</strong> → <strong>Apps</strong> → <strong>Attendance System</strong> খুলুন।",
"<strong>Permissions</strong> → <strong>Camera</strong> ট্যাপ করুন → <strong>Allow</strong> অথবা <strong>Allow while using the app</strong> নির্বাচন করুন।",
"App পুনরায় খুলুন এবং আবার scanning এর চেষ্টা করুন।"
]
},
{
"id": "clockin-troubleshoot",
"title": "Clock-in কাজ করছে না? দ্রুত checklist",
"steps": [
"<strong>Location</strong> ON করুন এবং app permission <strong>Allow while using the app</strong> সেট করুন (উপলব্ধ থাকলে <strong>Precise location</strong> সক্রিয় করুন)।",
"Network পরীক্ষা করুন: Wi-Fi অথবা data চালু আছে। <strong>Airplane mode</strong> off→on toggle করুন, তারপর আবার চেষ্টা করুন। হস্তক্ষেপ করলে VPN নিষ্ক্রিয় করুন।",
"নিশ্চিত করুন <strong>Automatic date &amp; time</strong> এবং <strong>time zone</strong> Android settings এ সক্রিয় আছে।",
"Force close করুন এবং app পুনরায় খুলুন। প্রয়োজনে, <strong>Attendance System</strong> cache clear করুন (Settings → Apps → Attendance System → Storage → Clear cache)।"
],
"note": "এখনও আটকে আছেন? একটি screenshot নিন এবং আপনার manager বা HR এর সাথে যোগাযোগ করুন।"
}
]
},
"ios": {
"heading": "iOS",
"comingSoon": "শীঘ্রই আসছে।"
}
},
"statusClockedIn": "আপনি ক্লক ইন করেছেন",
"statusClockedOut": "আপনি ক্লক আউট করেছেন",
"scanToClockIn": "ক্লক ইন করতে QR কোড স্ক্যান করুন",
"scanToClockOut": "ক্লক আউট করতে QR কোড স্ক্যান করুন",
"appInformation": "অ্যাপ তথ্য",
"version": "সংস্করণ",
"platform": "প্ল্যাটফর্ম",
"web": "ওয়েব"
}
+57 -2
View File
@@ -13,7 +13,6 @@
"english": "English", "english": "English",
"malay": "Bahasa Melayu", "malay": "Bahasa Melayu",
"setting": "Setting", "setting": "Setting",
"settings": "Settings",
"appInformation": "App Information", "appInformation": "App Information",
"version": "Version", "version": "Version",
"platform": "Platform", "platform": "Platform",
@@ -308,5 +307,61 @@
"deleteManagerFailed": "Failed to delete manager", "deleteManagerFailed": "Failed to delete manager",
"openCamera": "Open Camera", "openCamera": "Open Camera",
"scanQRCode": "Scan QR Code", "scanQRCode": "Scan QR Code",
"signOutOfAccount": "Sign Out of Account" "signOutOfAccount": "Sign Out of Account",
"darkMode": "Dark Mode",
"enableDarkMode": "Enable dark mode",
"disableDarkMode": "Disable dark mode",
"tamil": "Tamil",
"bengali": "Bengali",
"burmese": "Burmese",
"nepali": "Nepali",
"manualGuide": "Manual Guide",
"viewUserManual": "Read instructions and FAQs",
"manual": {
"android": {
"heading": "Android",
"faqs": [
{
"id": "android-location",
"title": "How to open location (Android)",
"steps": [
"Open <strong>Settings</strong> on your phone.",
"Go to <strong>Location</strong> <span class=\"text-sm text-gray-500\">(on some phones under <em>Security &amp; privacy</em>)</span>.",
"Turn <strong>Use location</strong> ON.",
"Open <strong>App permissions</strong> → find <strong>Nilai Clock</strong> → set to <strong>Allow while using the app</strong>.",
"Enable <strong>Precise location</strong> if available.",
"Return to the app and try clock-in again."
],
"note": "Names vary by brand: Samsung → Settings → Location → App permissions. Xiaomi → Settings → Location → Location services."
},
{
"id": "android-camera",
"title": "Enable camera permission (Android)",
"steps": [
"Open <strong>Settings</strong> → <strong>Apps</strong> → <strong>Nilai Clock</strong>.",
"Tap <strong>Permissions</strong> → <strong>Camera</strong> → choose <strong>Allow</strong> or <strong>Allow while using the app</strong>.",
"Reopen the app and try scanning again."
]
},
{
"id": "clockin-troubleshoot",
"title": "Clock-in not working? Quick checklist",
"steps": [
"Turn <strong>Location</strong> ON and set app permission to <strong>Allow while using the app</strong> (enable <strong>Precise location</strong> if available).",
"Check network: Wi-Fi or data is on. Toggle <strong>Airplane mode</strong> off→on, then retry. Disable VPN if it interferes.",
"Ensure <strong>Automatic date &amp; time</strong> and <strong>time zone</strong> are enabled in Android settings.",
"Force close and reopen the app. If needed, clear <strong>Attendance System</strong> cache (Settings → Apps → Nilai Clock → Storage → Clear cache)."
],
"note": "Still stuck? Take a screenshot and contact your manager or HR."
}
]
},
"ios": {
"heading": "iOS",
"comingSoon": "Coming soon."
}
}
} }
+59 -3
View File
@@ -13,7 +13,6 @@
"english": "English", "english": "English",
"malay": "Bahasa Melayu", "malay": "Bahasa Melayu",
"setting": "Tetapan", "setting": "Tetapan",
"settings": "Tetapan",
"appInformation": "Maklumat Aplikasi", "appInformation": "Maklumat Aplikasi",
"version": "Versi", "version": "Versi",
"platform": "Platform", "platform": "Platform",
@@ -220,7 +219,7 @@
"existingGeofences": "Kawasan Disimpan", "existingGeofences": "Kawasan Disimpan",
"view": "Lihat", "view": "Lihat",
"noGeofencesFound": "Tiada Geofences Dijumpai", "noGeofencesFound": "Tiada Geofences Dijumpai",
"startOver" : "Mula Semula", "startOver": "Mula Semula",
"workScheduleTitle": "Jadual Kerja", "workScheduleTitle": "Jadual Kerja",
"workScheduleDescription": "Klik pada tarikh untuk menukar statusnya. Hari yang didayakan berwarna hijau. Perubahan tidak akan disimpan sehingga anda mengklik 'Guna Perubahan'.", "workScheduleDescription": "Klik pada tarikh untuk menukar statusnya. Hari yang didayakan berwarna hijau. Perubahan tidak akan disimpan sehingga anda mengklik 'Guna Perubahan'.",
@@ -328,5 +327,62 @@
"deleteManagerFailed": "Gagal memadam Pentadbir", "deleteManagerFailed": "Gagal memadam Pentadbir",
"openCamera": "Buka Kamera", "openCamera": "Buka Kamera",
"scanQRCode": "Imbas Kod QR", "scanQRCode": "Imbas Kod QR",
"signOutOfAccount": "Log Keluar Akaun" "signOutOfAccount": "Log Keluar Akaun",
"darkMode": "Mod Gelap",
"enableDarkMode": "Aktifkan mod gelap",
"disableDarkMode": "Nyahaktifkan mod gelap",
"tamil": "Tamil",
"bengali": "Bengali",
"burmese": "Burmese",
"nepali": "Nepali",
"manualGuide": "Panduan Manual",
"viewUserManual": "Baca arahan dan Soalan Lazim",
"manual": {
"android": {
"heading": "Android",
"faqs": [
{
"id": "android-location",
"title": "Cara menghidupkan lokasi (Android)",
"steps": [
"Open <strong>Settings</strong> on your phone.",
"Go to <strong>Location</strong> <span class=\"text-sm text-gray-500\">(on some phones under <em>Security &amp; privacy</em>)</span>.",
"Turn <strong>Use location</strong> ON.",
"Open <strong>App permissions</strong> → find <strong>Nilai Clock</strong> → set to <strong>Allow while using the app</strong>.",
"Enable <strong>Precise location</strong> if available.",
"Return to the app and try clock-in again."
],
"note": "Nama menu mungkin berbeza mengikut jenama."
},
{
"id": "android-camera",
"title": "Benarkan kebenaran kamera (Android)",
"steps": [
"Open <strong>Settings</strong> → <strong>Apps</strong> → <strong>Nilai Clock</strong>.",
"Tap <strong>Permissions</strong> → <strong>Camera</strong> → choose <strong>Allow</strong> or <strong>Allow while using the app</strong>.",
"Reopen the app and try scanning again."
]
},
{
"id": "clockin-troubleshoot",
"title": "Clock-in tidak berjaya? Semakan pantas",
"steps": [
"Turn <strong>Location</strong> ON and set app permission to <strong>Allow while using the app</strong> (enable <strong>Precise location</strong> if available).",
"Check network: Wi-Fi or data is on. Toggle <strong>Airplane mode</strong> off→on, then retry. Disable VPN if it interferes.",
"Ensure <strong>Automatic date &amp; time</strong> and <strong>time zone</strong> are enabled in Android settings.",
"Force close and reopen the app. If needed, clear <strong>Attendance System</strong> cache (Settings → Apps → Nilai Clock → Storage → Clear cache)."
],
"note": "Jika masih gagal, ambil tangkapan skrin dan hubungi pengurus/HR."
}
]
},
"ios": {
"heading": "iOS",
"comingSoon": "Akan datang."
}
}
} }
+315
View File
@@ -0,0 +1,315 @@
{
"appTitle": "တက်ရောက်မှု စနစ်",
"logout": "ထွက်ရန်",
"login": "ဝင်ရောက်ရန်",
"username": "အသုံးပြုသူအမည်",
"password": "လျှို့ဝှက်နံပါတ်",
"loggingIn": "ဝင်ရောက်နေသည်...",
"language": "ဘာသာစကား",
"darkMode": "မှောင်မိုက်မုဒ်",
"toggleDarkMode": "အလင်းနှင့် မှောင်မိုက်အပြင်အဆင်များကြား ပြောင်းလဲရန်",
"failedConnection": "ဆာဗာနှင့် ချိတ်ဆက်၍မရပါ။",
"invalidToken": "ဆာဗာမှ မမှန်ကန်သော တိုကင်ရရှိခဲ့သည်။",
"invalidCredentials": "အသုံးပြုသူအမည် သို့မဟုတ် လျှို့ဝှက်နံပါတ် မမှန်ကန်ပါ။",
"english": "အင်္ဂလိပ်",
"malay": "မလေး",
"tamil": "တမီးလ်",
"bengali": "ဘင်္ဂါလီ",
"burmese": "မြန်မာ",
"nepali": "နီပေါ",
"yourStatus": "သင်၏အခြေအနေ",
"clockedIn": "အချိန်ဝင်ပြီး",
"clockedOut": "အချိန်ထွက်ပြီး",
"clockIn": "အချိန်ဝင်ရန်",
"clockOut": "အချိန်ထွက်ရန်",
"clock_in": "အချိန်ဝင်ရန်",
"clock_out": "အချိန်ထွက်ရန်",
"scanToClock": "{action} အတွက် စကင်န်ဖတ်ပါ",
"in": "ဝင်",
"out": "ထွက်",
"cancel": "ပယ်ဖျက်ရန်",
"viewMyClockHistory": "ကျွန်ုပ်၏ အချိန်မှတ်တမ်းကြည့်ရန်",
"changeMyPassword": "ကျွန်ုပ်၏ လျှို့ဝှက်နံပါတ်ပြောင်းရန်",
"myClockHistory": "ကျွန်ုပ်၏ အချိန်မှတ်တမ်း",
"backToDashboard": "ပင်မစာမျက်နှာသို့ ပြန်ရန်",
"noClockHistory": "သင့်တွင် အချိန်မှတ်တမ်း မရှိပါ။",
"clockHistoryFetchFail": "အချိန်မှတ်တမ်း ရယူ၍မရပါ:",
"viewClockHistory": "ကျွန်ုပ်၏ အချိန်မှတ်တမ်းကြည့်ရန်",
"changePassword": "လျှို့ဝှက်နံပါတ်ပြောင်းရန်",
"invalidCurrentPassword": "လက်ရှိလျှို့ဝှက်နံပါတ် မမှန်ကန်ပါ။",
"successClockIn": "အချိန်ဝင်ခြင်း အောင်မြင်ပါသည်။",
"successClockOut": "အချိန်ထွက်ခြင်း အောင်မြင်ပါသည်။",
"qrFail": "QR ကုဒ်ကို ဖတ်၍မရပါ။ ထပ်မံကြိုးစားပါ။",
"geoFail": "သင့်တည်နေရာကို ရယူ၍မရပါ: {message}။ တည်နေရာဝန်ဆောင်မှုများကို ဖွင့်ထားပါ။",
"successClock": "{location} တွင် အချိန်မှတ်ခြင်း အောင်မြင်ပါသည်။",
"changePasswordTitle": "လျှို့ဝှက်နံပါတ်ပြောင်းရန်",
"currentPassword": "လက်ရှိလျှို့ဝှက်နံပါတ်",
"newPassword": "လျှို့ဝှက်နံပါတ်အသစ်",
"confirmNewPassword": "လျှို့ဝှက်နံပါတ်အသစ် အတည်ပြုပါ",
"updating": "အပ်ဒိတ်လုပ်နေသည်...",
"tabPersonnel": "ဝန်ထမ်းများ",
"tabAttendance": "တက်ရောက်မှု",
"tabQrCodes": "QR ကုဒ်များ",
"uploadQrImage": "QR ပုံအပ်လုဒ်လုပ်ရန်",
"couldNotLoadWorkerInfo": "အလုပ်သမားအချက်အလက်များ ရယူ၍မရပါ",
"couldNotVerifyStatus": "ဆာဗာမှ လက်ရှိအခြေအနေကို အတည်ပြု၍မရပါ",
"successfullyClocked": "{action} အချိန်မှတ်ခြင်း အောင်မြင်ပါသည်",
"site": "နေရာ",
"errorOccurred": "အမှားအယွင်း ဖြစ်ပွားခဲ့သည်",
"unableToStartCamera": "ကင်မရာကို ဖွင့်၍မရပါ။",
"tryAgain": "ထပ်မံကြိုးစားပါ",
"qrDetectedGettingLocation": "QR ကုဒ် တွေ့ရှိပြီး။ တည်နေရာရယူနေသည်...",
"geolocationNotSupported": "သင့်ဘရောက်ဇာတွင် တည်နေရာဝန်ဆောင်မှု မပါဝင်ပါ။",
"unableToRetrieveLocation": "သင့်တည်နေရာကို ရယူ၍မရပါ: {message}။ တည်နေရာဝန်ဆောင်မှုများကို ဖွင့်ထားပါ။",
"qrNotDetectedTryAgain": "QR ကုဒ်ကို ဖတ်၍မရပါ။ ထပ်မံကြိုးစားပါ။",
"updatePassword": "လျှို့ဝှက်နံပါတ် အပ်ဒိတ်လုပ်ရန်",
"passwordsNoMatch": "လျှို့ဝှက်နံပါတ်အသစ်များ မတူညီပါ။",
"passwordTooShort": "လျှို့ဝှက်နံပါတ်အသစ်သည် အနည်းဆုံး ၆လုံး ရှိရပါမည်။",
"passwordUpdated": "လျှို့ဝှက်နံပါတ် အပ်ဒိတ်လုပ်ပြီးပါပြီ။ လျှို့ဝှက်နံပါတ်အသစ်ဖြင့် ဝင်ရောက်နိုင်ပါပြီ။",
"passwordUpdateError": "လျှို့ဝှက်နံပါတ် အပ်ဒိတ်လုပ်စဉ် အမှားအယွင်း ဖြစ်ပွားခဲ့သည်။",
"attendanceLogFor": "အတွက် တက်ရောက်မှုမှတ်တမ်း",
"addManualClockOut": "လက်ဖြင့် အချိန်ထွက် ထည့်သွင်းရန်",
"manualClockOutInstruction": "အလုပ်သမားက အချိန်ထွက်ရန် မေ့လျော့ပါက ဤပုံစံကို အသုံးပြုပါ။ နောက်ဆုံးဖြစ်ရပ်သည် အချိန်ဝင်ခြင်း ဖြစ်ရပါမည်။",
"clockOutTime": "အချိန်ထွက် အချိန်",
"reason": "အကြောင်းပြချက် (ဥပမာ \"အချိန်ထွက်ရန် မေ့လျော့ခဲ့သည်\")",
"enterBriefNote": "အကျဉ်းချုပ် မှတ်စုရေးပါ",
"addRecord": "မှတ်တမ်း ထည့်သွင်းရန်",
"startDate": "စတင်ရက်",
"endDate": "ပြီးဆုံးရက်",
"filterRecords": "မှတ်တမ်းများ စစ်ထုတ်ရန်",
"event": "ဖြစ်ရပ်",
"timestamp": "အချိန်တံဆိပ်",
"locationName": "နေရာအမည်",
"coordinates": "ကိုအော်ဒီနိတ်များ",
"notes": "မှတ်စုများ",
"noRecordsFound": "ဤကာလအတွင်း မှတ်တမ်းများ မတွေ့ပါ။",
"showOnMap": "မြေပုံပေါ်တွင် ပြရန်",
"nA": "မရှိ",
"pleaseSelectTimestamp": "အချိန်ထွက်အတွက် အချိန်တံဆိပ်ကို ရွေးချယ်ပါ။",
"pleaseProvideReason": "လက်ဖြင့်ထည့်သွင်းရန်အတွက် အကြောင်းပြချက်/မှတ်စု ပေးပါ။",
"manualClockOutSuccess": "လက်ဖြင့် အချိန်ထွက် မှတ်တမ်း အောင်မြင်စွာ ပြုလုပ်ပြီးပါပြီ။",
"manualClockOutError": "အမှားအယွင်း ဖြစ်ပွားခဲ့သည်: {message}",
"selectWorkers": "၁။ အလုပ်သမားများ ရွေးချယ်ရန်",
"searchWorkerPlaceholder": "အလုပ်သမား ရှာဖွေပါ...",
"selectAll": "အားလုံး ရွေးချယ်ရန်",
"addWorkersByTag": "တက်မှ အလုပ်သမားအားလုံးကို ထည့်ရန်",
"chooseTag": "-- တက် ရွေးချယ်ပါ --",
"addByTag": "တက်ဖြင့် ထည့်ရန်",
"selectedForReport": "အစီရင်ခံစာအတွက် ရွေးချယ်ပြီး ({count})",
"allWorkersSelected": "အလုပ်သမားအားလုံး ({count}) ရွေးချယ်ပြီး",
"noWorkersSelected": "အလုပ်သမား မရွေးချယ်ရသေးပါ။",
"reportSettings": "၂။ အစီရင်ခံစာ ဆက်တင်များ",
"setting": "ဆက်တင်",
"monthlySalary": "လစာ (ရူပီး)",
"salaryAppliedNote": "ရွေးချယ်ထားသော အလုပ်သမားအားလုံးအတွက် အသုံးပြုသည်။",
"salaryPlaceholder": "ဥပမာ ၃၀၀၀",
"otFactors": "အပိုအချိန် အချက်များ",
"weekendFactor": "စနေတနင်္ဂနွေ အချက်",
"holidayFactor": "အားလပ်ရက် အချက်",
"selectPublicHolidays": "အများပြည်သူ အားလပ်ရက်များ ရွေးချယ်ရန်",
"generateReport": "တက်ရောက်မှုနှင့် အပိုအချိန် အစီရင်ခံစာ ထုတ်လုပ်ရန်",
"overtimePaySummary": "အပိုအချိन် လစာအကျဉ်းချုပ်",
"exportOtSummary": "အပိုအချိန် အကျဉ်းချုပ် ပို့ထုတ်ရန် (CSV)",
"worker": "အလုပ်သမား",
"totalHoursWorked": "စုစုပေါင်း အလုပ်လုပ်ခဲ့သော နာရီများ",
"totalOtPay": "စုစုပေါင်း အပိုအချိန် လစာ (ရူပီး)",
"rawAttendanceData": "တက်ရောက်မှု အချက်အလက်များ",
"loadingReport": "အစီရင်ခံစာ ရယူနေသည်...",
"tagLoadError": "ရွေးချယ်ထားသော တက်အတွက် အလုပ်သမားများကို ရယူ၍မရပါ။",
"generateReportError": "အလုပ်သမားများ ရွေးချယ်ပါ၊ မှန်ကန်သော ရက်စွဲအပိုင်းအခြား သတ်မှတ်ပါ နှင့် လစာထည့်ပါ။",
"reportGenerationError": "အစီရင်ခံစာ ထုတ်လုပ်စဉ် အမှားအယွင်း ဖြစ်ပွားခဲ့သည်။",
"addNewUser": "အသုံးပြုသူအသစ် ထည့်သွင်းရန်",
"fullName": "အမည်အပြည့်အစုံ",
"egJohnSmith": "ဥပမာ မောင်ကျော်",
"egJsmith": "ဥပမာ mkyaw",
"eg123456": "ဥပမာ ၁၂၃၄၅၆",
"asManager": "မန်နေဂျာအနေဖြင့်",
"adding": "ထည့်သွင်းနေသည်...",
"addUser": "အသုံးပြုသူ ထည့်သွင်းရန်",
"manageTags": "တက်များ စီမံခန့်ခွဲရန်",
"createNewTag": "တက်အသစ် ဖန်တီးရန်",
"egTeam": "ဥပမာ အဖွဲ့",
"createTag": "တက် ဖန်တီးရန်",
"tags": "တက်များ",
"workerRoster": "အလုပ်သမား စာရင်း",
"searchByNameOrUsername": "အမည် သို့မဟုတ် အသုံးပြုသူအမည်ဖြင့် ရှာပါ",
"filterByTag": "တက်ဖြင့် စစ်ထုတ်ပါ",
"clearFilter": "စစ်ထုတ်မှု ရှင်းလင်းရန်",
"dateJoined": "ဝင်ရောက်သည့်ရက်",
"actions": "လုပ်ဆောင်ချက်များ",
"editTags": "တက်များ တည်းဖြတ်ရန်",
"viewRecords": "မှတ်တမ်းများ ကြည့်ရန်",
"delete": "ဖျက်ရန်",
"loadingWorkers": "အလုပ်သမားများ ရယူနေသည်...",
"noWorkersFound": "အလုပ်သမား မတွေ့ပါ။",
"previous": "ရှေ့သို့",
"next": "နောက်သို့",
"pageOf": "စာမျက်နှာ {current} / {total}",
"noTagsAvailable": "အသုံးပြုနိုင်သော တက် မရှိပါ။",
"done": "ပြီးပါပြီ",
"bulkEditTags": "တက်များ အစုလိုက် တည်းဖြတ်ရန်",
"clearSelection": "ရွေးချယ်မှု ရှင်းလင်းရန်",
"forUser": "အသုံးပြုသူအတွက်",
"savePassword": "လျှို့ဝှက်နံပါတ် သိမ်းဆည်းရန်",
"saving": "သိမ်းဆည်းနေသည်...",
"failedToUpdateTags": "တက်များ အပ်ဒိတ်လုပ်၍မရပါ။ ထပ်မံကြိုးစားပါ။",
"tagDeleted": "တက် ဖျက်ပြီးပါပြီ။",
"failedToFetchWorkers": "အလုပ်သမားများ ရယူ၍မရပါ။",
"failedToLoadPageData": "စာမျက်နှာ အချက်အလက်များ ရယူ၍မရပါ။",
"errorAddingUser": "အသုံးပြုသူ ထည့်သွင်းစဉ် အမှားအယွင်း ဖြစ်ပွားခဲ့သည်။",
"failedToDeleteWorker": "အလုပ်သမား ဖျက်၍မရပါ။",
"areYouSureDeleteWorker": "ဤအလုပ်သမား အကောင့်ကို ဖျက်မည်မှာ သေချာပါသလား။",
"areYouSureDeleteTag": "ဤတက်ကို ဖျက်မည်မှာ သေချာပါသလား။ ဤသည်က အလုပ်သမားအားလုံးမှ ဖယ်ရှားလိမ့်မည်။",
"failedToDeleteTag": "တက် ဖျက်၍မရပါ။",
"passwordsDoNotMatch": "လျှို့ဝှက်နံပါတ်များ မတူညီပါ။",
"createQrCode": "QR ကုဒ်အသစ် ဖန်တီးရန်",
"qrCodeName": "QR ကုဒ် အမည်",
"qrNamePlaceholder": "ဥပမာ 'အနောက်ဂိတ် ဝင်ပေါက်'",
"create": "ဖန်တီးရန်",
"newCodeCreated": "ကုဒ်အသစ် ဖန်တီးပြီးပါပြီ။",
"saveQrInstruction": "ဤပုံကို သိမ်းဆည်းပါ သို့မဟုတ် အောက်ပါ ID ကို အသုံးပြုပါ။ ဤသည်က ပြန်လည်ရယူမှုတွင် ပျောက်ကွယ်သွားလိမ့်မည်။",
"id": "အိုင်ဒီ",
"existingQrCodes": "ရှိပြီးသား QR ကုဒ်များ",
"name": "အမည်",
"status": "အခြေအနေ",
"deactivate": "ပိတ်ရန်",
"activate": "ဖွင့်ရန်",
"download": "ဒေါင်းလုဒ်လုပ်ရန်",
"noQrCodesFound": "QR ကုဒ် မတွေ့ပါ။ အထက်တွင် တစ်ခု ဖန်တီးပါ။",
"deleteQrConfirm": "ဤ QR ကုဒ်ကို ဖျက်မည်မှာ သေချာပါသလား။ ဤသည်ကို နောက်ပြန်မပြောင်းနိုင်ပါ။",
"qrDownloadError": "စိတ်မကောင်းပါ၊ QR ကုဒ်ကို ဒေါင်းလုဒ်လုပ်၍မရပါ။",
"rememberMe": "အလိုအလျောက်ဝင်ရောက်ရန် ကျွန်ုပ်ကို မှတ်ထားပါ",
"deviceNotAuthorized": "ဤစက်ပစ္စည်းသည် သင့်အကောင့်အတွက် ခွင့်ပြုချက် မရှိပါ။ သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။",
"locationTrackingActive": "နောက်ကွယ်တွင် တည်နေရာခြေရာခံမှု အသက်ဝင်နေသည်",
"securityCheckInProgress": "လုံခြုံရေးစစ်ဆေးမှု ပြုလုပ်နေသည်...",
"securityCheckComplete": "လုံခြုံရေးစစ်ဆေးမှု အောင်မြင်စွာ ပြီးဆုံးပါပြီ",
"highSecurityRisk": "မြင့်မားသော လုံခြုံရေးအန္တရာယ် တွေ့ရှိရသည်။ သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။",
"deviceRegistered": "စက်ပစ္စည်း အောင်မြင်စွာ မှတ်ပုံတင်ပြီးပါပြီ",
"autoLoginEnabled": "ဤစက်ပစ္စည်းအတွက် အလိုအလျောက်ဝင်ရောက်မှု ဖွင့်ပြီးပါပြီ",
"backgroundLocationEnabled": "နောက်ကွယ် တည်နေရာခြေရာခံမှု ဖွင့်ပြီးပါပြီ",
"permissionsRequired": "တက်ရောက်မှုခြေရာခံမှုအတွက် တည်နေရာခွင့်ပြုချက်များ လိုအပ်သည်",
"batteryOptimizationWarning": "ဆက်တိုက် တည်နေရာခြေရာခံမှုအတွက် ဤအပ်ပ်အတွက် ဘက်ထရီ အကောင်းဆုံးလုပ်ခြင်းကို ပိတ်ပါ",
"gpsSpooferDetected": "GPS အတုအပ် အပလီကေးရှင်း တွေ့ရှိရသည်။ ဤသည်က တက်ရောက်မှု တိကျမှုကို ထိခိုက်စေနိုင်သည်။",
"mockLocationEnabled": "အတု တည်နေရာ ဖွင့်ထားသည်။ တိကျသော တက်ရောက်မှု ခြေရာခံမှုအတွက် ပိတ်ပါ။",
"deviceSecurityWarning": "စက်ပစ္စည်း လုံခြုံရေး သတိပေးချက်: သံသယဖြစ်ဖွယ် အပလီကေးရှင်းများ တွေ့ရှိရသည်",
"locationUpdateFailed": "တည်နေရာ အပ်ဒိတ်လုပ်၍မရပါ။ အလိုအလျောက် ထပ်မံကြိုးစားလိမ့်မည်။",
"servicesInitializing": "မူလဝန်ဆောင်မှုများ စတင်နေသည်...",
"servicesReady": "ဝန်ဆောင်မှုများ အားလုံး အဆင်သင့်ပါပြီ",
"autoLoginFailed": "အလိုအလျောက်ဝင်ရောက်မှု မအောင်မြင်ပါ။ လက်ဖြင့် ဝင်ရောက်ပါ။",
"deviceValidationFailed": "စက်ပစ္စည်း အတည်ပြုခြင်း မအောင်မြင်ပါ။ ပံ့ပိုးမှုကို ဆက်သွယ်ပါ။",
"deviceMismatch": "ဤစက်ပစ္စည်းသည် သင့်အကောင့်အတွက် ခွင့်ပြုမထားပါ။",
"deviceRegistrationFailed": "စက်ပစ္စည်း မှတ်ပုံတင်မှု မအောင်မြင်ပါ။ ပြန်လည်ကြိုးစားပါ။",
"deviceRequired": "အလုပ်သမား အကောင့်ဝင်ရောက်မှုအတွက် စက်ပစ္စည်း မှတ်ပုံတင်ခြင်း လိုအပ်ပါသည်။",
"servicesStatus": "ဝန်ဆောင်မှုများ အခြေအနေ",
"overallStatus": "ခြုံငုံအခြေအနေ",
"locationTracking": "တည်နေရာခြေရာခံမှု",
"deviceRegistration": "စက်ပစ္စည်း မှတ်ပုံတင်မှု",
"securityStatus": "လုံခြုံရေး အခြေအနေ",
"lastLocationUpdate": "နောက်ဆုံး တည်နေရာ အပ်ဒိတ်",
"deviceId": "စက်ပစ္စည်း အိုင်ဒီ",
"start": "စတင်ရန်",
"check": "စစ်ဆေးရန်",
"checking": "စစ်ဆေးနေသည်...",
"refresh": "ပြန်လည်ရယူရန်",
"refreshing": "ပြန်လည်ရယူနေသည်...",
"notInitialized": "မစတင်ရသေးပါ",
"ready": "အဆင်သင့်",
"webOnly": "ဝက်ဘ်တွင်သာ",
"active": "လှုပ်ရှားနေသည်",
"inactive": "အလုပ်မလုပ်တော့ပါ",
"registered": "မှတ်ပုံတင်ပြီး",
"pending": "ဆိုင်းငံ့နေသည်",
"notChecked": "စစ်ဆေးထားခြင်း မရှိပါ",
"outdated": "အဆန်းပြားသွားပါပြီ",
"current": "လက်ရှိ",
"never": "မတိုင်မီ",
"justNow": "ယခုတင်",
"minutesAgo": "{minutes} မိနစ် အကြာ",
"hoursAgo": "{hours} နာရီ အကြာ",
"daysAgo": "{days} ရက် အကြာ",
"failedToRefreshStatus": "အခြေအနေ ပြန်လည်ရယူ၍မရပါ",
"locationTrackingStarted": "တည်နေရာခြေရာခံမှု အောင်မြင်စွာ စတင်သည်",
"failedToStartLocationTracking": "တည်နေရာခြေရာခံမှု စတင်၍မရပါ",
"securityCheckFailed": "လုံခြုံရေး စစ်ဆေးမှု မအောင်မြင်ပါ",
"personal": "ပုဂ္ဂိုလ်ရေး",
"clockHistory": "အချိန်မှတ်တမ်း",
"openCamera": "ကင်မရာ ဖွင့်ရန်",
"scanQRCode": "QR ကုဒ် စကင်ဖတ်ရန်",
"services": "ဝန်ဆောင်မှုများ",
"systemServicesStatus": "စနစ်ဝန်ဆောင်မှုများနှင့် လုံခြုံရေးအခြေအနေ",
"updateYourPassword": "သင့်အကောင့် လျှို့ဝှက်နံပါတ် အပ်ဒိတ်လုပ်ပါ",
"signOutOfAccount": "သင့်အကောင့်မှ ထွက်ရန်",
"workLocationTracking": "အလုပ်တည်နေရာ ခြေရာခံမှု",
"locationTrackingForAttendance": "အလုပ်တက်ရောက်မှုအတွက် တည်နေရာခြေရာခံမှု အသက်ဝင်နေသည်",
"monitoringLocation": "အလုပ်တက်ရောက်မှုအတွက် တည်နေရာကို ကြည့်ရှုနေသည်",
"manualGuide": "လက်စွဲလမ်းညွှန်",
"viewUserManual": "လမ်းညွှန်ချက်များနှင့် FAQs ကိုဖတ်ပါ",
"manual": {
"android": {
"heading": "Android",
"faqs": [
{
"id": "android-location",
"title": "Location ကို ဖွင့်နည်း (Android)",
"steps": [
"သင့်ဖုန်းရှိ <strong>Settings</strong> ကို ဖွင့်ပါ။",
"<strong>Location</strong> သို့သွားပါ <span class=\"text-sm text-gray-500\">(အချို့ဖုန်းများတွင် <em>Security &amp; privacy</em> အောက်တွင်)</span>။",
"<strong>Use location</strong> ကို ON ဖွင့်ပါ။",
"<strong>App permissions</strong> ကိုဖွင့်ပါ → <strong>Attendance System</strong> ကိုရှာပါ → <strong>Allow while using the app</strong> သတ်မှတ်ပါ။",
"ရနိုင်လျှင် <strong>Precise location</strong> ကို ဖွင့်ပါ။",
"App သို့ပြန်သွားပြီး clock-in ကို ထပ်စမ်းကြည့်ပါ။"
],
"note": "အမှတ်တံဆိပ်ပေါ်မူတည်၍ အမည်များကွဲပြားနိုင်သည်: Samsung → Settings → Location → App permissions. Xiaomi → Settings → Location → Location services။"
},
{
"id": "android-camera",
"title": "Camera permission ဖွင့်နည်း (Android)",
"steps": [
"<strong>Settings</strong> → <strong>Apps</strong> → <strong>Attendance System</strong> ကို ဖွင့်ပါ။",
"<strong>Permissions</strong> → <strong>Camera</strong> ကို နှိပ်ပါ → <strong>Allow</strong> သို့မဟုတ် <strong>Allow while using the app</strong> ကို ရွေးပါ။",
"App ကို ပြန်ဖွင့်ပြီး scanning ကို ထပ်စမ်းကြည့်ပါ။"
]
},
{
"id": "clockin-troubleshoot",
"title": "Clock-in အလုပ်မလုပ်ဘူးလား? အမြန် checklist",
"steps": [
"<strong>Location</strong> ကို ON ဖွင့်ပြီး app permission ကို <strong>Allow while using the app</strong> သတ်မှတ်ပါ (ရနိုင်လျှင် <strong>Precise location</strong> ကို ဖွင့်ပါ)။",
"Network ကို စစ်ဆေးပါ: Wi-Fi သို့မဟုတ် data ကို ဖွင့်ထားသည်။ <strong>Airplane mode</strong> ကို off→on toggle လုပ်ပါ၊ ပြီးနောက် ထပ်စမ်းကြည့်ပါ။ ဝင်စွက်နေလျှင် VPN ကို ပိတ်ပါ။",
"Android settings တွင် <strong>Automatic date &amp; time</strong> နှင့် <strong>time zone</strong> တို့ကို ဖွင့်ထားကြောင်း သေချာပါစေ။",
"Force close လုပ်ပြီး app ကို ပြန်ဖွင့်ပါ။ လိုအပ်လျှင် <strong>Attendance System</strong> cache ကို clear လုပ်ပါ (Settings → Apps → Attendance System → Storage → Clear cache)။"
],
"note": "ဆက်လက်ပြဿနာရှိနေပါသလား? Screenshot ရိုက်ပြီး သင့် manager သို့မဟုတ် HR ကို ဆက်သွယ်ပါ။"
}
]
},
"ios": {
"heading": "iOS",
"comingSoon": "မကြာမီ ရောက်ရှိပါမည်။"
}
},
"statusClockedIn": "သင်သည် ရုံးဝင်ထားပါသည်",
"statusClockedOut": "သင်သည် ရုံးထွက်ထားပါသည်",
"scanToClockIn": "ရုံးဝင်ရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ",
"scanToClockOut": "ရုံးထွက်ရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ",
"appInformation": "एप जानकारी",
"version": "संस्करण",
"platform": "प्लेटफर्म",
"web": "वेब"
}
+315
View File
@@ -0,0 +1,315 @@
{
"appTitle": "उपस्थिति प्रणाली",
"logout": "लगआउट",
"login": "लगइन",
"username": "प्रयोगकर्ता नाम",
"password": "पासवर्ड",
"loggingIn": "लगइन गर्दै...",
"language": "भाषा",
"darkMode": "डार्क मोड",
"toggleDarkMode": "उज्यालो र अँध्यारो थिमहरू बीच स्विच गर्नुहोस्",
"failedConnection": "सर्भरसँग जडान गर्न सकिएन।",
"invalidToken": "सर्भरबाट अमान्य टोकन प्राप्त भयो।",
"invalidCredentials": "गलत प्रयोगकर्ता नाम वा पासवर्ड।",
"english": "अंग्रेजी",
"malay": "मलय",
"tamil": "तामिल",
"bengali": "बङ्गाली",
"burmese": "बर्मेली",
"nepali": "नेपाली",
"yourStatus": "स्थिति",
"clockedIn": "उपस्थित भएको",
"clockedOut": "अनुपस्थित भएको",
"clockIn": "उपस्थित हुनुहोस्",
"clockOut": "अनुपस्थित हुनुहोस्",
"clock_in": "उपस्थित हुनुहोस्",
"clock_out": "अनुपस्थित हुनुहोस्",
"scanToClock": "{action} को लागि स्क्यान गर्नुहोस्",
"in": "भित्र",
"out": "बाहिर",
"cancel": "रद्द गर्नुहोस्",
"viewMyClockHistory": "मेरो उपस्थिति इतिहास हेर्नुहोस्",
"changeMyPassword": "मेरो पासवर्ड परिवर्तन गर्नुहोस्",
"myClockHistory": "मेरो उपस्थिति इतिहास",
"backToDashboard": "ड्यासबोर्डमा फर्कनुहोस्",
"noClockHistory": "तपाईंको कुनै उपस्थिति इतिहास छैन।",
"clockHistoryFetchFail": "उपस्थिति इतिहास ल्याउन सकिएन:",
"viewClockHistory": "मेरो उपस्थिति इतिहास हेर्नुहोस्",
"changePassword": "मेरो पासवर्ड परिवर्तन गर्नुहोस्",
"invalidCurrentPassword": "गलत वर्तमान पासवर्ड।",
"successClockIn": "सफलतापूर्वक उपस्थित भएको।",
"successClockOut": "सफलतापूर्वक अनुपस्थित भएको।",
"qrFail": "QR कोड पत्ता लगाउन सकिएन। कृपया पुनः प्रयास गर्नुहोस्।",
"geoFail": "तपाईंको स्थान पत्ता लगाउन सकिएन: {message}। कृपया स्थान सेवाहरू सक्षम पार्नुहोस्।",
"successClock": "{location} मा सफलतापूर्वक उपस्थित भएको।",
"changePasswordTitle": "पासवर्ड परिवर्तन गर्नुहोस्",
"currentPassword": "वर्तमान पासवर्ड",
"newPassword": "नयाँ पासवर्ड",
"confirmNewPassword": "नयाँ पासवर्ड पुष्टि गर्नुहोस्",
"updating": "अपडेट गर्दै...",
"tabPersonnel": "कर्मचारी",
"tabAttendance": "उपस्थिति",
"tabQrCodes": "QR कोडहरू",
"uploadQrImage": "QR छवि अपलोड गर्नुहोस्",
"couldNotLoadWorkerInfo": "कर्मचारी जानकारी लोड गर्न सकिएन",
"couldNotVerifyStatus": "सर्भरबाट वर्तमान स्थिति प्रमाणित गर्न सकिएन",
"successfullyClocked": "सफलतापूर्वक {action} भएको",
"site": "साइट",
"errorOccurred": "त्रुटि भयो",
"unableToStartCamera": "क्यामेरा सुरु गर्न सकिएन।",
"tryAgain": "पुनः प्रयास गर्नुहोस्",
"qrDetectedGettingLocation": "QR कोड पत्ता लाग्यो। स्थान प्राप्त गर्दै...",
"geolocationNotSupported": "तपाईंको ब्राउजरले भौगोलिक स्थान समर्थन गर्दैन।",
"unableToRetrieveLocation": "तपाईंको स्थान पत्ता लगाउन सकिएन: {message}। कृपया स्थान सेवाहरू सक्षम पार्नुहोस्।",
"qrNotDetectedTryAgain": "QR कोड पत्ता लगाउन सकिएन। कृपया पुनः प्रयास गर्नुहोस्।",
"updatePassword": "पासवर्ड अपडेट गर्नुहोस्",
"passwordsNoMatch": "नयाँ पासवर्डहरू मेल खाँदैनन्।",
"passwordTooShort": "नयाँ पासवर्ड कम्तिमा ६ वर्णको हुनुपर्छ।",
"passwordUpdated": "पासवर्ड सफलतापूर्वक अपडेट भयो! तपाईं अब आफ्नो नयाँ पासवर्ड प्रयोग गरेर लगइन गर्न सक्नुहुन्छ।",
"passwordUpdateError": "पासवर्ड अपडेट गर्दा त्रुटि भयो।",
"attendanceLogFor": "को लागि उपस्थिति लग",
"addManualClockOut": "म्यानुअल क्लक-आउट थप्नुहोस्",
"manualClockOutInstruction": "यदि कर्मचारी क्लक आउट गर्न बिर्सिएका छन् भने यो फारम प्रयोग गर्नुहोस्। अन्तिम घटना क्लक-इन हुनुपर्छ।",
"clockOutTime": "क्लक-आउट समय",
"reason": "कारण (जस्तै, \"क्लक आउट गर्न बिर्सियो\")",
"enterBriefNote": "छोटो टिप्पणी प्रविष्ट गर्नुहोस्",
"addRecord": "रेकर्ड थप्नुहोस्",
"startDate": "सुरु मिति",
"endDate": "अन्त्य मिति",
"filterRecords": "रेकर्डहरू फिल्टर गर्नुहोस्",
"event": "घटना",
"timestamp": "समय छाप",
"locationName": "स्थानको नाम",
"coordinates": "निर्देशाङ्क",
"notes": "टिप्पणीहरू",
"noRecordsFound": "यस अवधिको लागि कुनै रेकर्ड भेटिएन।",
"showOnMap": "नक्सामा देखाउनुहोस्",
"nA": "उपलब्ध छैन",
"pleaseSelectTimestamp": "कृपया क्लक-आउटको लागि समय छाप चयन गर्नुहोस्।",
"pleaseProvideReason": "कृपया म्यानुअल प्रविष्टिको लागि कारण/टिप्पणी प्रदान गर्नुहोस्।",
"manualClockOutSuccess": "म्यानुअल क्लक-आउट सफलतापूर्वक रेकर्ड भयो!",
"manualClockOutError": "त्रुटि भयो: {message}",
"selectWorkers": "१. कर्मचारी चयन गर्नुहोस्",
"searchWorkerPlaceholder": "कर्मचारी खोज्नुहोस्...",
"selectAll": "सबै चयन गर्नुहोस्",
"addWorkersByTag": "ट्यागबाट सबै कर्मचारी थप्नुहोस्",
"chooseTag": "-- ट्याग छान्नुहोस् --",
"addByTag": "ट्यागद्वारा थप्नुहोस्",
"selectedForReport": "रिपोर्टको लागि चयनित ({count})",
"allWorkersSelected": "सबै कर्मचारी ({count}) चयनित",
"noWorkersSelected": "कुनै कर्मचारी चयनित छैन।",
"reportSettings": "२. रिपोर्ट सेटिङहरू",
"setting": "सेटिङ",
"monthlySalary": "मासिक तलब (RM)",
"salaryAppliedNote": "सबै चयनित कर्मचारीहरूमा लागू।",
"salaryPlaceholder": "जस्तै, ३०००",
"otFactors": "OT फ्याक्टरहरू",
"weekendFactor": "सप्ताहन्त फ्याक्टर",
"holidayFactor": "छुट्टी फ्याक्टर",
"selectPublicHolidays": "सार्वजनिक छुट्टीहरू चयन गर्नुहोस्",
"generateReport": "उपस्थिति र OT रिपोर्ट उत्पन्न गर्नुहोस्",
"overtimePaySummary": "ओभरटाइम भुक्तानी सारांश",
"exportOtSummary": "OT सारांश निर्यात गर्नुहोस् (CSV)",
"worker": "कर्मचारी",
"totalHoursWorked": "कुल काम गरेको घण्टा",
"totalOtPay": "कुल OT भुक्तानी (RM)",
"rawAttendanceData": "कच्चा उपस्थिति डेटा",
"loadingReport": "रिपोर्ट लोड गर्दै...",
"tagLoadError": "चयनित ट्यागको लागि कर्मचारी लोड गर्न सकिएन।",
"generateReportError": "कृपया कर्मचारी चयन गर्नुहोस्, मान्य मिति दायरा सेट गर्नुहोस्, र तलब प्रविष्ट गर्नुहोस्।",
"reportGenerationError": "रिपोर्ट उत्पन्न गर्दा त्रुटि भयो।",
"addNewUser": "नयाँ प्रयोगकर्ता थप्नुहोस्",
"fullName": "पूरा नाम",
"egJohnSmith": "जस्तै जोन स्मिथ",
"egJsmith": "जस्तै jsmith",
"eg123456": "जस्तै १२३४५६",
"asManager": "प्रबन्धकको रूपमा",
"adding": "थप्दै...",
"addUser": "प्रयोगकर्ता थप्नुहोस्",
"manageTags": "ट्यागहरू व्यवस्थापन गर्नुहोस्",
"createNewTag": "नयाँ ट्याग सिर्जना गर्नुहोस्",
"egTeam": "जस्तै टिम",
"createTag": "ट्याग सिर्जना गर्नुहोस्",
"tags": "ट्यागहरू",
"workerRoster": "कर्मचारी सूची",
"searchByNameOrUsername": "नाम वा प्रयोगकर्ता नामद्वारा खोज्नुहोस्",
"filterByTag": "ट्यागद्वारा फिल्टर गर्नुहोस्",
"clearFilter": "फिल्टर हटाउनुहोस्",
"dateJoined": "सामेल भएको मिति",
"actions": "कार्यहरू",
"editTags": "ट्यागहरू सम्पादन गर्नुहोस्",
"viewRecords": "रेकर्डहरू हेर्नुहोस्",
"delete": "मेटाउनुहोस्",
"loadingWorkers": "कर्मचारी लोड गर्दै...",
"noWorkersFound": "कुनै कर्मचारी भेटिएन।",
"previous": "अघिल्लो",
"next": "अर्को",
"pageOf": "पृष्ठ {current} को {total}",
"noTagsAvailable": "कुनै ट्याग उपलब्ध छैन।",
"done": "सकियो",
"bulkEditTags": "बल्क ट्याग सम्पादन",
"clearSelection": "चयन हटाउनुहोस्",
"forUser": "प्रयोगकर्ताको लागि",
"savePassword": "पासवर्ड सेभ गर्नुहोस्",
"saving": "सेभ गर्दै...",
"failedToUpdateTags": "ट्यागहरू अपडेट गर्न सकिएन। कृपया पुनः प्रयास गर्नुहोस्।",
"tagDeleted": "ट्याग सफलतापूर्वक मेटाइयो।",
"failedToFetchWorkers": "कर्मचारी फेच गर्न सकिएन।",
"failedToLoadPageData": "पृष्ठ डेटा लोड गर्न सकिएन।",
"errorAddingUser": "प्रयोगकर्ता थप्दा त्रुटि भयो।",
"failedToDeleteWorker": "कर्मचारी मेटाउन सकिएन।",
"areYouSureDeleteWorker": "के तपाईं यो कर्मचारी खाता मेटाउन निश्चित हुनुहुन्छ?",
"areYouSureDeleteTag": "के तपाईं यो ट्याग मेटाउन निश्चित हुनुहुन्छ? यसले सबै कर्मचारीहरूबाट यसलाई हटाउनेछ।",
"failedToDeleteTag": "ट्याग मेटाउन सकिएन।",
"passwordsDoNotMatch": "पासवर्डहरू मेल खाँदैनन्।",
"createQrCode": "नयाँ QR कोड सिर्जना गर्नुहोस्",
"qrCodeName": "QR कोड नाम",
"qrNamePlaceholder": "जस्तै, 'पश्चिम गेट प्रवेश'",
"create": "सिर्जना गर्नुहोस्",
"newCodeCreated": "नयाँ कोड सिर्जना भयो!",
"saveQrInstruction": "यो छवि सेभ गर्नुहोस् वा तलको ID प्रयोग गर्नुहोस्। यो रिफ्रेसमा हराउनेछ।",
"id": "ID",
"existingQrCodes": "अवस्थित QR कोडहरू",
"name": "नाम",
"status": "स्थिति",
"deactivate": "निष्क्रिय पार्नुहोस्",
"activate": "सक्रिय पार्नुहोस्",
"download": "डाउनलोड गर्नुहोस्",
"noQrCodesFound": "कुनै QR कोडहरू भेटिएन। माथि एउटा सिर्जना गर्नुहोस्!",
"deleteQrConfirm": "के तपाईं यो QR कोड मेटाउन निश्चित हुनुहुन्छ? यो पूर्ववत गर्न सकिँदैन।",
"qrDownloadError": "माफ गर्नुहोस्, QR कोड डाउनलोड गर्न सकिएन।",
"rememberMe": "अटो-लगइनको लागि मलाई सम्झनुहोस्",
"deviceNotAuthorized": "यो उपकरण तपाईंको खाताको लागि प्राधिकृत छैन। कृपया आफ्नो व्यवस्थापकलाई सम्पर्क गर्नुहोस्।",
"locationTrackingActive": "पृष्ठभूमिमा स्थान ट्र्याकिङ सक्रिय छ",
"securityCheckInProgress": "सुरक्षा जाँच भइरहेको छ...",
"securityCheckComplete": "सुरक्षा जाँच सफलतापूर्वक सम्पन्न भयो",
"highSecurityRisk": "उच्च सुरक्षा जोखिम पत्ता लाग्यो। कृपया आफ्नो व्यवस्थापकलाई सम्पर्क गर्नुहोस्।",
"deviceRegistered": "उपकरण सफलतापूर्वक दर्ता भयो",
"autoLoginEnabled": "यस उपकरणको लागि अटो-लगइन सक्षम पारियो",
"backgroundLocationEnabled": "पृष्ठभूमि स्थान ट्र्याकिङ सक्षम पारियो",
"permissionsRequired": "उपस्थिति ट्र्याकिङको लागि स्थान अनुमतिहरू आवश्यक छ",
"batteryOptimizationWarning": "निरन्तर स्थान ट्र्याकिङ सुनिश्चित गर्न कृपया यस एपको लागि ब्याट्री अप्टिमाइजेसन अक्षम पार्नुहोस्",
"gpsSpooferDetected": "GPS स्पूफिङ एप्लिकेसन पत्ता लाग्यो। यसले उपस्थिति शुद्धतालाई असर गर्न सक्छ।",
"mockLocationEnabled": "नक्कली स्थान सक्षम छ। सही उपस्थिति ट्र्याकिङको लागि कृपया यसलाई अक्षम पार्नुहोस्।",
"deviceSecurityWarning": "उपकरण सुरक्षा चेतावनी: संदिग्ध एप्लिकेसनहरू पत्ता लाग्यो",
"locationUpdateFailed": "स्थान अपडेट गर्न सकिएन। स्वचालित रूपमा पुनः प्रयास गर्नेछ।",
"servicesInitializing": "मूल सेवाहरू प्रारम्भ गर्दै...",
"servicesReady": "सबै सेवाहरू तयार छन्",
"autoLoginFailed": "अटो-लगइन असफल। कृपया म्यानुअल रूपमा लगइन गर्नुहोस्।",
"deviceValidationFailed": "उपकरण प्रमाणीकरण असफल। कृपया सहयोगलाई सम्पर्क गर्नुहोस्।",
"deviceMismatch": "यो उपकरण तपाईंको खाताको लागि अधिकृत छैन।",
"deviceRegistrationFailed": "उपकरण दर्ता असफल। फेरि प्रयास गर्नुहोस्।",
"deviceRequired": "कामदार लगइनको लागि उपकरण दर्ता आवश्यक छ।",
"servicesStatus": "सेवाहरूको स्थिति",
"overallStatus": "समग्र स्थिति",
"locationTracking": "स्थान ट्र्याकिङ",
"deviceRegistration": "उपकरण दर्ता",
"securityStatus": "सुरक्षा स्थिति",
"lastLocationUpdate": "अन्तिम स्थान अपडेट",
"deviceId": "उपकरण ID",
"start": "सुरु गर्नुहोस्",
"check": "जाँच गर्नुहोस्",
"checking": "जाँच गर्दै...",
"refresh": "रिफ्रेस गर्नुहोस्",
"refreshing": "रिफ्रेस गर्दै...",
"notInitialized": "प्रारम्भ गरिएको छैन",
"ready": "तयार",
"webOnly": "वेब मात्र",
"active": "सक्रिय",
"inactive": "निष्क्रिय",
"registered": "दर्ता गरिएको",
"pending": "पेन्डिङ",
"notChecked": "जाँच गरिएको छैन",
"outdated": "पुरानो",
"current": "वर्तमान",
"never": "कहिल्यै छैन",
"justNow": "भर्खरै",
"minutesAgo": "{minutes} मिनेट अघि",
"hoursAgo": "{hours} घण्टा अघि",
"daysAgo": "{days} दिन अघि",
"failedToRefreshStatus": "स्थिति रिफ्रेस गर्न सकिएन",
"locationTrackingStarted": "स्थान ट्र्याकिङ सफलतापूर्वक सुरु भयो",
"failedToStartLocationTracking": "स्थान ट्र्याकिङ सुरु गर्न सकिएन",
"securityCheckFailed": "सुरक्षा जाँच असफल",
"personal": "व्यक्तिगत",
"clockHistory": "उपस्थिति इतिहास",
"openCamera": "क्यामेरा खोल्नुहोस्",
"scanQRCode": "QR कोड स्क्यान गर्नुहोस्",
"services": "सेवाहरू",
"systemServicesStatus": "प्रणाली सेवाहरू र सुरक्षा स्थिति",
"updateYourPassword": "तपाईंको खाताको पासवर्ड अपडेट गर्नुहोस्",
"signOutOfAccount": "तपाईंको खाताबाट साइन आउट गर्नुहोस्",
"workLocationTracking": "कार्य स्थान ट्र्याकिङ",
"locationTrackingForAttendance": "कार्य उपस्थितिको लागि स्थान ट्र्याकिङ सक्रिय",
"monitoringLocation": "कार्य उपस्थितिको लागि स्थान निगरानी गर्दै",
"manualGuide": "म्यानुअल गाइड",
"viewUserManual": "निर्देशनहरू र FAQs पढ्नुहोस्",
"manual": {
"android": {
"heading": "Android",
"faqs": [
{
"id": "android-location",
"title": "Location कसरी खोल्ने (Android)",
"steps": [
"आफ्नो फोनमा <strong>Settings</strong> खोल्नुहोस्।",
"<strong>Location</strong> मा जानुहोस् <span class=\"text-sm text-gray-500\">(केहि फोनहरूमा <em>Security &amp; privacy</em> अन्तर्गत)</span>।",
"<strong>Use location</strong> ON गर्नुहोस्।",
"<strong>App permissions</strong> खोल्नुहोस् → <strong>Attendance System</strong> फेला पार्नुहोस् → <strong>Allow while using the app</strong> सेट गर्नुहोस्।",
"उपलब्ध भए <strong>Precise location</strong> सक्षम गर्नुहोस्।",
"App मा फर्कनुहोस् र फेरि clock-in प्रयास गर्नुहोस्।"
],
"note": "ब्रान्ड अनुसार नामहरू फरक हुन्छन्: Samsung → Settings → Location → App permissions. Xiaomi → Settings → Location → Location services।"
},
{
"id": "android-camera",
"title": "Camera permission सक्षम गर्नुहोस् (Android)",
"steps": [
"<strong>Settings</strong> → <strong>Apps</strong> → <strong>Attendance System</strong> खोल्नुहोस्।",
"<strong>Permissions</strong> → <strong>Camera</strong> ट्याप गर्नुहोस् → <strong>Allow</strong> वा <strong>Allow while using the app</strong> छान्नुहोस्।",
"App पुन: खोल्नुहोस् र फेरि scanning प्रयास गर्नुहोस्।"
]
},
{
"id": "clockin-troubleshoot",
"title": "Clock-in काम गरिरहेको छैन? द्रुत checklist",
"steps": [
"<strong>Location</strong> ON गर्नुहोस् र app permission लाई <strong>Allow while using the app</strong> सेट गर्नुहोस् (उपलब्ध भए <strong>Precise location</strong> सक्षम गर्नुहोस्)।",
"Network जाँच गर्नुहोस्: Wi-Fi वा data खुला छ। <strong>Airplane mode</strong> off→on toggle गर्नुहोस्, त्यसपछि फेरि प्रयास गर्नुहोस्। हस्तक्षेप गरेमा VPN निष्क्रिय गर्नुहोस्।",
"Android settings मा <strong>Automatic date &amp; time</strong> र <strong>time zone</strong> सक्षम छन् भनी सुनिश्चित गर्नुहोस्।",
"Force close गर्नुहोस् र app पुन: खोल्नुहोस्। आवश्यक भएमा, <strong>Attendance System</strong> cache clear गर्नुहोस् (Settings → Apps → Attendance System → Storage → Clear cache)।"
],
"note": "अझै अड्किनु भयो? Screenshot लिनुहोस् र आफ्नो manager वा HR लाई सम्पर्क गर्नुहोस्।"
}
]
},
"ios": {
"heading": "iOS",
"comingSoon": "चाँडै आउँदैछ।"
}
},
"statusClockedIn": "तपाईं क्लक इन हुनुभएको छ",
"statusClockedOut": "तपाईं क्लक आउट हुनुभएको छ",
"scanToClockIn": "क्लक इन गर्न QR कोड स्क्यान गर्नुहोस्",
"scanToClockOut": "क्लक आउट गर्न QR कोड स्क्यान गर्नुहोस्",
"appInformation": "एप जानकारी",
"version": "संस्करण",
"platform": "प्लेटफर्म",
"web": "वेब"
}
+301
View File
@@ -0,0 +1,301 @@
{
"appTitle": "வருகை அமைப்பு",
"logout": "வெளியேறு",
"login": "உள்நுழை",
"username": "பயனர் பெயர்",
"password": "கடவுச்சொல்",
"loggingIn": "உள்நுழைகிறது...",
"language": "மொழி",
"darkMode": "இருண்ட பயன்முறை",
"toggleDarkMode": "வெளிச்சம் மற்றும் இருண்ட தீம்களுக்கு இடையில் மாற்றவும்",
"failedConnection": "சர்வருடன் இணைக்க முடியவில்லை.",
"invalidToken": "சர்வரிலிருந்து தவறான டோக்கன் பெறப்பட்டது.",
"invalidCredentials": "தவறான பயனர் பெயர் அல்லது கடவுச்சொல்.",
"english": "ஆங்கிலம்",
"malay": "மலாய்",
"tamil": "தமிழ்",
"bengali": "பெங்காலி",
"burmese": "பர்மா",
"nepali": "நேபாளி",
"yourStatus": "நிலைமை",
"clockedIn": "வருகை பதிவு செய்யப்பட்டது",
"clockedOut": "வெளியேறல் பதிவு செய்யப்பட்டது",
"clockIn": "வருகை பதிவு",
"clockOut": "வெளியேறல் பதிவு",
"clock_in": "வருகை பதிவு",
"clock_out": "வெளியேறல் பதிவு",
"scanToClock": "{action} பதிவு செய்ய ஸ்கேன் செய்யவும்",
"in": "வருகை",
"out": "வெளியேறல்",
"cancel": "ரத்து",
"viewMyClockHistory": "என் வருகை வரலாற்றைப் பார்க்கவும்",
"changeMyPassword": "என் கடவுச்சொல்லை மாற்றவும்",
"myClockHistory": "என் வருகை வரலாறு",
"backToDashboard": "டாஷ்போர்டுக்குத் திரும்பு",
"noClockHistory": "உங்களுக்கு வருகை வரலாறு இல்லை.",
"clockHistoryFetchFail": "வருகை வரலாற்றை பெற முடியவில்லை:",
"viewClockHistory": "என் வருகை வரலாற்றைப் பார்க்கவும்",
"changePassword": "என் கடவுச்சொல்லை மாற்றவும்",
"invalidCurrentPassword": "தற்போதைய கடவுச்சொல் தவறானது.",
"successClockIn": "வெற்றிகரமாக வருகை பதிவு செய்யப்பட்டது.",
"successClockOut": "வெற்றிகரமாக வெளியேறல் பதிவு செய்யப்பட்டது.",
"qrFail": "QR கோட்டைக் கண்டறிய முடியவில்லை. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.",
"geoFail": "உங்கள் இருப்பிடத்தைப் பெற முடியவில்லை: {message}. தயவுசெய்து இருப்பிட சேவைகளை இயக்கவும்.",
"successClock": "{location} இல் வெற்றிகரமாக பதிவு செய்யப்பட்டது.",
"changePasswordTitle": "கடவுச்சொல்லை மாற்று",
"currentPassword": "தற்போதைய கடவுச்சொல்",
"newPassword": "புதிய கடவுச்சொல்",
"confirmNewPassword": "புதிய கடவுச்சொல்லை உறுதிப்படுத்து",
"updating": "புதுப்பிக்கிறது...",
"tabPersonnel": "பணியாளர்கள்",
"tabAttendance": "வருகை",
"tabQrCodes": "QR கோட்கள்",
"uploadQrImage": "QR படத்தைப் பதிவேற்று",
"couldNotLoadWorkerInfo": "பணியாளர் தகவலைப் பெற முடியவில்லை",
"couldNotVerifyStatus": "சர்வரிலிருந்து தற்போதைய நிலைமையை சரிபார்க்க முடியவில்லை",
"successfullyClocked": "வெற்றிகரமாக {action} பதிவு செய்யப்பட்டது",
"site": "இடத்தில்",
"errorOccurred": "பிழை நிகழ்ந்தது",
"unableToStartCamera": "கேமராவைத் தொடங்க முடியவில்லை.",
"tryAgain": "மீண்டும் முயற்சிக்கவும்",
"qrDetectedGettingLocation": "QR கோட் கண்டறியப்பட்டது. இருப்பிடத்தைப் பெறுகிறது...",
"geolocationNotSupported": "உங்கள் உலாவியால் புவியிருப்பிடம் ஆதரிக்கப்படவில்லை.",
"unableToRetrieveLocation": "உங்கள் இருப்பிடத்தைப் பெற முடியவில்லை: {message}. தயவுசெய்து இருப்பிட சேவைகளை இயக்கவும்.",
"qrNotDetectedTryAgain": "QR கோட்டைக் கண்டறிய முடியவில்லை. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.",
"updatePassword": "கடவுச்சொல்லைப் புதுப்பிக்கவும்",
"passwordsNoMatch": "புதிய கடவுச்சொற்கள் பொருந்தவில்லை.",
"passwordTooShort": "புதிய கடவுச்சொல் குறைந்தது 6 எழுத்துக்களாக இருக்க வேண்டும்.",
"passwordUpdated": "கடவுச்சொல் வெற்றிகரமாக புதுப்பிக்கப்பட்டது! நீங்கள் இப்போது உங்கள் புதிய கடவுச்சொல்லை உள்நுழைய பயன்படுத்தலாம்.",
"passwordUpdateError": "கடவுச்சொல்லை புதுப்பிக்கும்போது பிழை ஏற்பட்டது.",
"attendanceLogFor": "வருகை பதிவு -",
"addManualClockOut": "கைமுறை வெளியேறல் பதிவு சேர்க்கவும்",
"manualClockOutInstruction": "பணியாளர் வெளியேறல் பதிவு செய்ய மறந்தால் இந்த படிவத்தைப் பயன்படுத்தவும். கடைசி நிகழ்வு வருகை பதிவாக இருக்க வேண்டும்.",
"clockOutTime": "வெளியேறல் நேரம்",
"reason": "காரணம் (எ.கா., \"வெளியேறல் பதிவு செய்ய மறந்துவிட்டார்\")",
"enterBriefNote": "சுருக்கமான குறிப்பை உள்ளிடவும்",
"addRecord": "பதிவு சேர்க்கவும்",
"startDate": "தொடக்க தேதி",
"endDate": "இறுதி தேதி",
"filterRecords": "பதிவுகளை வடிகட்டு",
"event": "நிகழ்வு",
"timestamp": "நேர முத்திரை",
"locationName": "இடத்தின் பெயர்",
"coordinates": "ஆயத்தொலைவுகள்",
"notes": "குறிப்புகள்",
"noRecordsFound": "இந்த காலத்திற்கான பதிவுகள் எதுவும் கிடைக்கவில்லை.",
"showOnMap": "வரைபடத்தில் காட்டு",
"nA": "கிடையாது",
"pleaseSelectTimestamp": "தயவுசெய்து வெளியேறலுக்கான நேர முத்திரையைத் தேர்ந்தெடுக்கவும்.",
"pleaseProvideReason": "தயவுசெய்து கைமுறை பதிவிற்கான காரணம்/குறிப்பை வழங்கவும்.",
"manualClockOutSuccess": "கைமுறை வெளியேறல் வெற்றிகரமாக பதிவு செய்யப்பட்டது!",
"manualClockOutError": "பிழை ஏற்பட்டது: {message}",
"selectWorkers": "1. பணியாளர்களைத் தேர்ந்தெடுக்கவும்",
"searchWorkerPlaceholder": "ஒரு பணியாளரைத் தேடவும்...",
"selectAll": "அனைத்தையும் தேர்ந்தெடு",
"addWorkersByTag": "ஒரு டேக்கிலிருந்து அனைத்து பணியாளர்களையும் சேர்க்கவும்",
"chooseTag": "-- ஒரு டேக்கைத் தேர்ந்தெடுக்கவும் --",
"addByTag": "டேக் மூலம் சேர்க்கவும்",
"selectedForReport": "அறிக்கைக்காக தேர்ந்தெடுக்கப்பட்டவை ({count})",
"allWorkersSelected": "அனைத்து பணியாளர்கள் ({count}) தேர்ந்தெடுக்கப்பட்டனர்",
"noWorkersSelected": "பணியாளர்கள் எதுவும் தேர்ந்தெடுக்கப்படவில்லை.",
"reportSettings": "2. அறிக்கை அமைப்புகள்",
"setting": "அமைப்பு",
"monthlySalary": "மாதச் சம்பளம் (RM)",
"salaryAppliedNote": "தேர்ந்தெடுக்கப்பட்ட அனைத்து பணியாளர்களுக்கும் பயன்படுத்தப்படும்.",
"salaryPlaceholder": "எ.கா., 3000",
"otFactors": "மேல்நேர காரணிகள்",
"weekendFactor": "வாரக்கடைசி காரணி",
"holidayFactor": "விடுமுறை காரணி",
"selectPublicHolidays": "பொது விடுமுறைகளைத் தேர்ந்தெடுக்கவும்",
"generateReport": "வருகை & மேல்நேர அறிக்கையை உருவாக்கு",
"overtimePaySummary": "மேல்நேர ஊதிய சுருக்கம்",
"exportOtSummary": "மேல்நேர சுருக்கத்தை ஏற்றுமதி செய் (CSV)",
"worker": "பணியாளர்",
"totalHoursWorked": "மொத்த வேலை நேரங்கள்",
"totalOtPay": "மொத்த மேல்நேர ஊதியம் (RM)",
"rawAttendanceData": "மூல வருகை தரவு",
"loadingReport": "அறிக்கை ஏற்றுகிறது...",
"tagLoadError": "தேர்ந்தெடுக்கப்பட்ட டேக்கிற்கான பணியாளர்களை ஏற்ற முடியவில்லை.",
"generateReportError": "தயவுசெய்து பணியாளர்களைத் தேர்ந்தெடுத்து, சரியான தேதி வரம்பை அமைத்து, சம்பளத்தை உள்ளிடவும்.",
"reportGenerationError": "அறிக்கையை உருவாக்கும்போது பிழை ஏற்பட்டது.",
"addNewUser": "புதிய பயனரைச் சேர்க்கவும்",
"fullName": "முழு பெயர்",
"egJohnSmith": "எ.கா. John Smith",
"egJsmith": "எ.கா. jsmith",
"eg123456": "எ.கா. 123456",
"asManager": "மேலாளராக",
"adding": "சேர்க்கிறது...",
"addUser": "பயனரைச் சேர்க்கவும்",
"manageTags": "டேக்குகளை நிர்வகிக்கவும்",
"createNewTag": "புதிய டேக் உருவாக்கவும்",
"egTeam": "எ.கா. குழு",
"createTag": "டேக் உருவாக்கவும்",
"tags": "டேக்குகள்",
"workerRoster": "பணியாளர் பட்டியல்",
"searchByNameOrUsername": "பெயர் அல்லது பயனர் பெயர் மூலம் தேடவும்",
"filterByTag": "டேக் மூலம் வடிகட்டவும்",
"clearFilter": "வடிகட்டியைத் துடைக்கவும்",
"dateJoined": "சேர்ந்த தேதி",
"actions": "செயல்கள்",
"editTags": "டேக்குகளைத் திருத்து",
"viewRecords": "பதிவுகளைப் பார்க்கவும்",
"delete": "நீக்கு",
"loadingWorkers": "பணியாளர்கள் ஏற்றப்படுகிறது...",
"noWorkersFound": "பணியாளர்கள் எதுவும் கிடைக்கவில்லை.",
"previous": "முந்தைய",
"next": "அடுத்த",
"pageOf": "பக்கம் {current} / {total}",
"noTagsAvailable": "டேக்குகள் எதுவும் கிடைக்கவில்லை.",
"done": "முடிந்தது",
"bulkEditTags": "பல டேக்குகளைத் திருத்து",
"clearSelection": "தேர்வைத் துடைக்கவும்",
"forUser": "பயனருக்கு",
"savePassword": "கடவுச்சொல்லைச் சேமிக்கவும்",
"saving": "சேமிக்கிறது...",
"failedToUpdateTags": "டேக்குகளை புதுப்பிக்க முடியவில்லை. தயவுசெய்து மீண்டும் முயற்சிக்கவும்.",
"tagDeleted": "டேக் வெற்றிகரமாக நீக்கப்பட்டது.",
"failedToFetchWorkers": "பணியாளர்களைப் பெற முடியவில்லை.",
"failedToLoadPageData": "பக்க தரவை ஏற்ற முடியவில்லை.",
"errorAddingUser": "பயனரைச் சேர்க்கும்போது பிழை ஏற்பட்டது.",
"failedToDeleteWorker": "பணியாளரை நீக்க முடியவில்லை.",
"areYouSureDeleteWorker": "இந்த பணியாளர் கணக்கை நீக்க நீங்கள் உறுதியாக உள்ளீர்களா?",
"areYouSureDeleteTag": "இந்த டேக்கை நீக்க நீங்கள் உறுதியாக உள்ளீர்களா? இது அனைத்து பணியாளர்களிடமிருந்தும் அகற்றப்படும்.",
"failedToDeleteTag": "டேக்கை நீக்க முடியவில்லை.",
"passwordsDoNotMatch": "கடவுச்சொற்கள் பொருந்தவில்லை.",
"createQrCode": "புதிய QR கோட் உருவாக்கவும்",
"qrCodeName": "QR கோட்டின் பெயர்",
"qrNamePlaceholder": "எ.கா., 'மேற்கு வாயில் நுழைவாயில்'",
"create": "உருவாக்கு",
"newCodeCreated": "புதிய கோட் உருவாக்கப்பட்டது!",
"saveQrInstruction": "இந்த படத்தைச் சேமிக்கவும் அல்லது கீழே உள்ள ID ஐப் பயன்படுத்தவும். இது புதுப்பிப்பில் மறைந்துவிடும்.",
"id": "ஐடி",
"existingQrCodes": "ஏற்கனவே உள்ள QR கோட்கள்",
"name": "பெயர்",
"status": "நிலைமை",
"deactivate": "செயலிழக்கச் செய்",
"activate": "செயல்படுத்து",
"download": "பதிவிறக்கு",
"noQrCodesFound": "QR கோட்கள் எதுவும் கிடைக்கவில்லை. மேலே ஒன்றை உருவாக்கவும்!",
"deleteQrConfirm": "இந்த QR கோட்டை நீக்க நீங்கள் உறுதியாக உள்ளீர்களா? இதை மாற்ற முடியாது.",
"qrDownloadError": "மன்னிக்கவும், QR கோட்டைப் பதிவிறக்க முடியவில்லை.",
"rememberMe": "தானியங்கு உள்நுழைவுக்காக என்னை நினைவில் வைக்கவும்",
"deviceNotAuthorized": "இந்த சாதனம் உங்கள் கணக்குக்கு அங்கீகரிக்கப்படவில்லை. தயவுசெய்து உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளவும்.",
"locationTrackingActive": "இருப்பிட கண்காணிப்பு பின்னணியில் செயல்படுகிறது",
"securityCheckInProgress": "பாதுகாப்பு சரிபார்ப்பு நடைபெறுகிறது...",
"securityCheckComplete": "பாதுகாப்பு சரிபார்ப்பு வெற்றிகரமாக முடிவடைந்தது",
"highSecurityRisk": "அதிக பாதுகாப்பு அபாயம் கண்டறியப்பட்டது. தயவுசெய்து உங்கள் நிர்வாகியைத் தொடர்பு கொள்ளவும்.",
"deviceRegistered": "சாதனம் வெற்றிகரமாக பதிவு செய்யப்பட்டது",
"autoLoginEnabled": "இந்த சாதனத்திற்கு தானியங்கு உள்நுழைவு இயக்கப்பட்டது",
"backgroundLocationEnabled": "பின்னணி இருப்பிட கண்காணிப்பு இயக்கப்பட்டது",
"permissionsRequired": "வருகை கண்காணிப்புக்கு இருப்பிட அனுமதிகள் தேவை",
"batteryOptimizationWarning": "தொடர்ச்சியான இருப்பிட கண்காணிப்பை உறுதிப்படுத்த இந்த பயன்பாட்டிற்கு பேட்டரி மேம்படுத்தலை முடக்கவும்",
"gpsSpooferDetected": "GPS போலிப் பயன்பாடு கண்டறியப்பட்டது. இது வருகை துல்லியத்தைப் பாதிக்கலாம்.",
"mockLocationEnabled": "போலி இருப்பிடம் இயக்கப்பட்டுள்ளது. துல்லியமான வருகை கண்காணிப்புக்கு அதை முடக்கவும்.",
"deviceSecurityWarning": "சாதன பாதுகாப்பு எச்சரிக்கை: சந்தேகத்திற்குரிய பயன்பாடுகள் கண்டறியப்பட்டன",
"locationUpdateFailed": "இருப்பிடத்தை புதுப்பிக்க முடியவில்லை. தானாகவே மீண்டும் முயற்சிக்கும்.",
"servicesInitializing": "அடிப்படை சேவைகளை துவக்குகிறது...",
"servicesReady": "அனைத்து சேவைகளும் தயாராக உள்ளன",
"autoLoginFailed": "தானியங்கு உள்நுழைவு தோல்வி. தயவுசெய்து கைமுறையாக உள்நுழைக.",
"deviceValidationFailed": "சாதன சரிபார்ப்பு தோல்வி. தயவுசெய்து ஆதரவைத் தொடர்பு கொள்ளவும்.",
"deviceMismatch": "இந்த சாதனம் உங்கள் கணக்கிற்கு அங்கீகரிக்கப்படவில்லை.",
"deviceRegistrationFailed": "சாதன பதிவு தோல்வி. மீண்டும் முயற்சிக்கவும்.",
"deviceRequired": "தொழிலாளர் உள்நுழைவிற்கு சாதன பதிவு தேவை.",
"servicesStatus": "சேவைகளின் நிலை",
"overallStatus": "ஒட்டுமொத்த நிலை",
"locationTracking": "இருப்பிட கண்காணிப்பு",
"deviceRegistration": "சாதன பதிவு",
"securityStatus": "பாதுகாப்பு நிலை",
"lastLocationUpdate": "கடைசி இருப்பிட புதுப்பிப்பு",
"deviceId": "சாதன ஐடி",
"start": "தொடங்கு",
"check": "சரிபார்க்கவும்",
"checking": "சரிபார்க்கிறது...",
"refresh": "புதுப்பிக்கவும்",
"refreshing": "புதுப்பிக்கிறது...",
"notInitialized": "துவக்கப்படவில்லை",
"ready": "தயார்",
"webOnly": "வலை மட்டும்",
"active": "செயலில்",
"inactive": "செயலில் இல்லை",
"registered": "பதிவு செய்யப்பட்டது",
"pending": "நிலுவையில்",
"notChecked": "சரிபார்க்கப்படவில்லை",
"outdated": "காலாவதியானது",
"current": "தற்போதைய",
"never": "ஒருபோதும் இல்லை",
"justNow": "இப்போதுதான்",
"minutesAgo": "{minutes} நி முன்",
"hoursAgo": "{hours} மணி முன்",
"daysAgo": "{days} நா முன்",
"failedToRefreshStatus": "நிலையை புதுப்பிக்க முடியவில்லை",
"locationTrackingStarted": "இருப்பிட கண்காணிப்பு வெற்றிகரமாக தொடங்கப்பட்டது",
"failedToStartLocationTracking": "இருப்பிட கண்காணிப்பைத் தொடங்க முடியவில்லை",
"securityCheckFailed": "பாதுகாப்பு சரிபார்ப்பு தோல்வி",
"personal": "தனிப்பட்ட",
"clockHistory": "வருகை வரலாறு",
"openCamera": "கேமராவைத் திற",
"scanQRCode": "QR கோட் ஸ்கேன் செய்",
"services": "சேவைகள்",
"systemServicesStatus": "அமைப்பு சேவைகள் மற்றும் பாதுகாப்பு நிலை",
"updateYourPassword": "உங்கள் கணக்கு கடவுச்சொல்லை புதுப்பிக்கவும்",
"signOutOfAccount": "உங்கள் கணக்கிலிருந்து வெளியேறவும்",
"workLocationTracking": "பணியிட இருப்பிட கண்காணிப்பு",
"locationTrackingForAttendance": "பணி வருகைக்காக இருப்பிட கண்காணிப்பு செயலில்",
"monitoringLocation": "பணி வருகைக்காக இருப்பிடத்தைக் கண்காணிக்கிறது",
"manualGuide": "கையேடு வழிகாட்டி",
"viewUserManual": "வழிமுறைகள் மற்றும் FAQs ஐ படிக்கவும்",
"manual": {
"android": {
"heading": "Android",
"faqs": [
{
"id": "android-location",
"title": "Location ஐ எப்படி திறப்பது (Android)",
"steps": [
"உங்கள் தொலைபேசியில் <strong>Settings</strong> ஐத் திறக்கவும்.",
"<strong>Location</strong> க்குச் செல்லவும் <span class=\"text-sm text-gray-500\">(சில தொலைபேசிகளில் <em>Security &amp; privacy</em> கீழ்)</span>.",
"<strong>Use location</strong> ஐ ON செய்யவும்.",
"<strong>App permissions</strong> ஐத் திறக்கவும் → <strong>Attendance System</strong> ஐக் கண்டறியவும் → <strong>Allow while using the app</strong> என அமைக்கவும்.",
"கிடைத்தால் <strong>Precise location</strong> ஐ இயக்கவும்.",
"ஆப்பிற்குத் திரும்பி மீண்டும் clock-in செய்ய முயற்சிக்கவும்."
],
"note": "பிராண்டுகளுக்கு ஏற்ப பெயர்கள் மாறுபடும்: Samsung → Settings → Location → App permissions. Xiaomi → Settings → Location → Location services."
},
{
"id": "android-camera",
"title": "Camera permission ஐ இயக்குவது எப்படி (Android)",
"steps": [
"<strong>Settings</strong> → <strong>Apps</strong> → <strong>Attendance System</strong> ஐத் திறக்கவும்.",
"<strong>Permissions</strong> → <strong>Camera</strong> ஐத் தட்டவும் → <strong>Allow</strong> அல்லது <strong>Allow while using the app</strong> தேர்ந்தெடுக்கவும்.",
"ஆப்பை மீண்டும் திறந்து scanning செய்ய முயற்சிக்கவும்."
]
},
{
"id": "clockin-troubleshoot",
"title": "Clock-in வேலை செய்யவில்லையா? விரைவு checklist",
"steps": [
"<strong>Location</strong> ஐ ON செய்து app permission ஐ <strong>Allow while using the app</strong> என அமைக்கவும் (கிடைத்தால் <strong>Precise location</strong> ஐ இயக்கவும்).",
"Network ஐ சரிபார்க்கவும்: Wi-Fi அல்லது data இயக்கத்தில் உள்ளது. <strong>Airplane mode</strong> ஐ off→on toggle செய்து, பின்னர் மீண்டும் முயற்சிக்கவும். தடையாக இருந்தால் VPN ஐ முடக்கவும்.",
"Android settings இல் <strong>Automatic date &amp; time</strong> மற்றும் <strong>time zone</strong> இயக்கப்பட்டுள்ளதா என உறுதிப்படுத்தவும்.",
"Force close செய்து app ஐ மீண்டும் திறக்கவும். தேவைப்பட்டால், <strong>Attendance System</strong> cache ஐ clear செய்யவும் (Settings → Apps → Attendance System → Storage → Clear cache)."
],
"note": "இன்னும் சிக்கலா? Screenshot எடுத்து உங்கள் manager அல்லது HR ஐ தொடர்பு கொள்ளவும்."
}
]
},
"ios": {
"heading": "iOS",
"comingSoon": "விரைவில் வரும்."
}
},
"statusClockedIn": "நீங்கள் வேலைக்கு வந்துள்ளீர்கள்",
"statusClockedOut": "நீங்கள் வேலையை விட்டு வெளியேறியுள்ளீர்கள்",
"scanToClockIn": "வேலைக்கு வர QR குறியீட்டை ஸ்கேன் செய்யவும்",
"scanToClockOut": "வெளியேற QR குறியீட்டை ஸ்கேன் செய்யவும்",
"appInformation": "செயலி தகவல்",
"version": "பதிப்பு",
"platform": "தளம்"
}
+3
View File
@@ -1,3 +1,4 @@
console.log("[DEBUG] main.js loaded");
import './assets/main.css' import './assets/main.css'
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
@@ -10,3 +11,5 @@ const app = createApp(App)
app.use(router) app.use(router)
app.use(i18n) app.use(i18n)
app.mount('#app') app.mount('#app')
console.log("[DEBUG] i18n import in main.js:", i18n);
+7
View File
@@ -54,6 +54,13 @@ const router = createRouter({
component: ManagerPermissions, component: ManagerPermissions,
meta: { requiresAuth: true, role: 'manager' }, meta: { requiresAuth: true, role: 'manager' },
}, },
{
path: '/worker/manual-guide',
name: 'ManualGuide',
component: () => import('@/views/ManualGuide.vue'),
meta: { requiresAuth: true, role: 'worker' }
},
], ],
}) })
+10 -6
View File
@@ -232,12 +232,16 @@ const exportRawRecords = async () => {
exportLoading.value = true; exportLoading.value = true;
const { startDate, endDate } = filters.value; const { startDate, endDate } = filters.value;
// pull preferred tz from localStorage; fall back to browser tz
const tz = localStorage.getItem('tz') || Intl.DateTimeFormat().resolvedOptions().timeZone;
try { try {
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/managers/attendance-records/export-raw?startDate=${startDate}&endDate=${endDate}&workerIds=${workerId}`, { const response = await fetch(
headers: { `${import.meta.env.VITE_API_BASE_URL}/api/managers/attendance-records/export-raw?startDate=${startDate}&endDate=${endDate}&workerIds=${workerId}&tz=${encodeURIComponent(tz)}`,
'Authorization': `Bearer ${sessionStorage.getItem('token')}` {
} headers: { 'Authorization': `Bearer ${sessionStorage.getItem('token')}` }
}); }
);
if (!response.ok) throw new Error('Network response was not ok.'); if (!response.ok) throw new Error('Network response was not ok.');
const blob = await response.blob(); const blob = await response.blob();
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
@@ -251,7 +255,7 @@ const exportRawRecords = async () => {
} catch (_err) { } catch (_err) {
alert('Failed to export records.'); alert('Failed to export records.');
} finally { } finally {
exportLoading.value = false; exportLoading.value = false;
} }
}; };
+75
View File
@@ -0,0 +1,75 @@
<template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
<!-- Match other pages' blue header -->
<header class="fixed left-0 right-0 top-0 z-50 bg-blue-600 text-white shadow-lg">
<div class="px-4 py-6" :style="`padding-top: calc(var(--safe-area-inset-top) + 1.5rem);`">
<div class="flex items-center">
<button @click="goBack" class="mr-4 p-2 hover:bg-blue-700 rounded-lg transition-colors" aria-label="Back">
<ArrowLeftIcon class="w-6 h-6" />
</button>
<h1 class="text-3xl font-bold">{{ $t('manualGuide') }}</h1>
</div>
</div>
</header>
<main class="main-with-fixed-header-and-nav p-4 space-y-4">
<!-- Android Group -->
<section class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm overflow-hidden">
<div class="px-5 pt-5 pb-3">
<h2 class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">
{{ $t('manual.android.heading') }}
</h2>
</div>
<div class="divide-y divide-gray-200 dark:divide-gray-700">
<div v-for="item in faqsAndroid" :key="item.id">
<details class="group">
<summary
class="flex items-center justify-between cursor-pointer select-none p-5 focus:outline-none
focus-visible:ring-2 focus-visible:ring-blue-500"
>
<span class="text-lg font-semibold text-gray-900 dark:text-gray-100">
{{ item.title }}
</span>
<ChevronDownIcon class="w-5 h-5 text-gray-500 transition-transform group-open:rotate-180" />
</summary>
<div class="px-5 pb-5 pt-0 text-gray-700 dark:text-gray-300">
<ol v-if="item.steps" class="list-decimal pl-5 space-y-2">
<li v-for="(s, i) in item.steps" :key="i" v-html="s"></li>
</ol>
<p v-if="item.note" class="mt-3 text-sm text-gray-500 dark:text-gray-400" v-html="item.note"></p>
</div>
</details>
</div>
</div>
</section>
<!-- iOS Group -->
<section class="bg-white dark:bg-gray-800 rounded-2xl shadow-sm overflow-hidden">
<div class="px-5 pt-5 pb-3">
<h2 class="text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">
{{ $t('manual.ios.heading') }}
</h2>
</div>
<div class="p-5 text-gray-700 dark:text-gray-300">
<p class="text-sm">{{ $t('manual.ios.comingSoon') }}</p>
</div>
</section>
</main>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { ChevronDownIcon, ArrowLeftIcon } from '@heroicons/vue/24/outline'
import { useRouter } from 'vue-router'
const router = useRouter()
const goBack = () => router.back()
// Use i18n arrays (we already added these keys in en/ms)
const { tm } = useI18n()
const faqsAndroid = computed(() => tm('manual.android.faqs') || [])
</script>
+28 -9
View File
@@ -2,11 +2,9 @@
<div class="mobile-viewport bg-gray-100 dark:bg-gray-900 min-h-screen"> <div class="mobile-viewport bg-gray-100 dark:bg-gray-900 min-h-screen">
<!-- Return Button (same position as settings button in dashboard) --> <!-- Return Button (same position as settings button in dashboard) -->
<div class="fixed bottom-4 right-4 z-50"> <div class="fixed bottom-4 right-4 z-50">
<button <button @click="goBack"
@click="goBack"
class="bg-white dark:bg-gray-800 shadow-lg rounded-full p-3 hover:shadow-xl transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500" class="bg-white dark:bg-gray-800 shadow-lg rounded-full p-3 hover:shadow-xl transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500"
aria-label="Return to Dashboard" aria-label="Return to Dashboard">
>
<svg class="w-6 h-6 text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6 text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
@@ -31,7 +29,8 @@
<!-- Settings Menu --> <!-- Settings Menu -->
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-lg overflow-hidden"> <div class="bg-white dark:bg-gray-800 rounded-2xl shadow-lg overflow-hidden">
<!-- Clock History --> <!-- Clock History -->
<router-link to="/worker/history" class="flex items-center p-5 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"> <router-link to="/worker/history"
class="flex items-center p-5 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/50 rounded-xl flex items-center justify-center mr-5"> <div class="w-12 h-12 bg-blue-100 dark:bg-blue-900/50 rounded-xl flex items-center justify-center mr-5">
<ChartBarIcon class="w-8 h-8 text-blue-600 dark:text-blue-400" /> <ChartBarIcon class="w-8 h-8 text-blue-600 dark:text-blue-400" />
</div> </div>
@@ -43,7 +42,8 @@
</router-link> </router-link>
<!-- Change Password --> <!-- Change Password -->
<router-link to="/worker/change-password" class="flex items-center p-5 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"> <router-link to="/worker/change-password"
class="flex items-center p-5 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<div class="w-12 h-12 bg-orange-100 dark:bg-orange-900/50 rounded-xl flex items-center justify-center mr-5"> <div class="w-12 h-12 bg-orange-100 dark:bg-orange-900/50 rounded-xl flex items-center justify-center mr-5">
<LockClosedIcon class="w-8 h-8 text-orange-600 dark:text-orange-400" /> <LockClosedIcon class="w-8 h-8 text-orange-600 dark:text-orange-400" />
</div> </div>
@@ -61,12 +61,30 @@
</div> </div>
<div class="flex-grow"> <div class="flex-grow">
<h3 class="font-semibold text-lg text-gray-900 dark:text-gray-100 mb-2">{{ $t('language') }}</h3> <h3 class="font-semibold text-lg text-gray-900 dark:text-gray-100 mb-2">{{ $t('language') }}</h3>
<select v-model="currentLang" @change="changeLang" class="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"> <select v-model="currentLang" @change="changeLang"
class="w-full px-4 py-3 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="en">{{ $t('english') }}</option> <option value="en">{{ $t('english') }}</option>
<option value="ms">{{ $t('malay') }}</option> <option value="ms">{{ $t('malay') }}</option>
<option value="tm">{{ $t('tamil') }}</option>
<option value="bd">{{ $t('bengali') }}</option>
<option value="my">{{ $t('burmese') }}</option>
<option value="np">{{ $t('nepali') }}</option>
</select> </select>
</div> </div>
</div> </div>
<!-- Manual Guide (NEW) -->
<router-link to="/worker/manual-guide"
class="flex items-center p-5 border-t border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
<div class="w-12 h-12 bg-green-100 dark:bg-green-900/50 rounded-xl flex items-center justify-center mr-5">
<BookOpenIcon class="w-8 h-8 text-green-600 dark:text-green-400" />
</div>
<div class="flex-grow">
<h3 class="font-semibold text-lg text-gray-900 dark:text-gray-100">{{ $t('manualGuide') }}</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">{{ $t('viewUserManual') }}</p>
</div>
<ChevronRightIcon class="w-6 h-6 text-gray-400" />
</router-link>
</div> </div>
<!-- App Information --> <!-- App Information -->
@@ -85,7 +103,8 @@
</div> </div>
<!-- Logout Button --> <!-- Logout Button -->
<button @click="logout" class="w-full flex items-center justify-center p-5 bg-white dark:bg-gray-800 rounded-2xl shadow-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"> <button @click="logout"
class="w-full flex items-center justify-center p-5 bg-white dark:bg-gray-800 rounded-2xl shadow-lg hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
<div class="w-12 h-12 bg-red-100 dark:bg-red-900/50 rounded-xl flex items-center justify-center mr-5"> <div class="w-12 h-12 bg-red-100 dark:bg-red-900/50 rounded-xl flex items-center justify-center mr-5">
<ArrowRightOnRectangleIcon class="w-8 h-8 text-red-600 dark:text-red-400" /> <ArrowRightOnRectangleIcon class="w-8 h-8 text-red-600 dark:text-red-400" />
</div> </div>
@@ -102,7 +121,7 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { ChartBarIcon, LockClosedIcon, LanguageIcon, ArrowRightOnRectangleIcon, ChevronRightIcon, UserIcon } from '@heroicons/vue/24/outline' import { ChartBarIcon, LockClosedIcon, LanguageIcon, ArrowRightOnRectangleIcon, ChevronRightIcon, UserIcon, BookOpenIcon } from '@heroicons/vue/24/outline'
// Removed authService dependency for web migration // Removed authService dependency for web migration
const { locale } = useI18n() const { locale } = useI18n()