From 6c17840a878f94bf0723be106a17fcd611dad127 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 06:14:40 +0000 Subject: [PATCH 1/3] Initial plan From 279e9acdb909d27102188a0919cc2caf23f3ab47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 06:23:04 +0000 Subject: [PATCH 2/3] Add i18n infrastructure and language support for Turkish and English Co-authored-by: saracmert <5950989+saracmert@users.noreply.github.com> --- src/_locales/en/messages.json | 172 ++++++++++++++++++++++++++++++++++ src/_locales/tr/messages.json | 172 ++++++++++++++++++++++++++++++++++ src/background.js | 43 +++++---- src/i18n.js | 66 +++++++++++++ src/manifest.json | 5 +- src/options.html | 36 ++++--- src/options.js | 32 ++++++- src/popup.html | 39 +++++--- src/popup.js | 35 +++++-- 9 files changed, 544 insertions(+), 56 deletions(-) create mode 100644 src/_locales/en/messages.json create mode 100644 src/_locales/tr/messages.json create mode 100644 src/i18n.js diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json new file mode 100644 index 0000000..4e85f7a --- /dev/null +++ b/src/_locales/en/messages.json @@ -0,0 +1,172 @@ +{ + "extName": { + "message": "Jira Bug Reporter" + }, + "extDescription": { + "message": "Collects error and network information from the active page and creates a Jira ticket." + }, + "popupTitle": { + "message": "Jira Bug Reporter" + }, + "popupSubtitle": { + "message": "Collect screenshot and logs from this page and create a Jira ticket with one click." + }, + "labelCookies": { + "message": "Cookies" + }, + "hintCookies": { + "message": "Include cookies" + }, + "labelStorage": { + "message": "Storage Summary" + }, + "hintStorage": { + "message": "Include localStorage & sessionStorage summaries" + }, + "btnCreateIssue": { + "message": "Create Jira Issue" + }, + "btnCreateIssueTitle": { + "message": "Create new Jira issue" + }, + "statusCollecting": { + "message": "Collecting information..." + }, + "statusCreated": { + "message": "Created:" + }, + "statusError": { + "message": "Error:" + }, + "statusUnknown": { + "message": "Unknown" + }, + "optionsTitle": { + "message": "Jira Bug Reporter Settings" + }, + "optionsSubtitle": { + "message": "Configure the required settings for the extension to work." + }, + "labelJiraBaseUrl": { + "message": "Jira Server URL:" + }, + "placeholderJiraBaseUrl": { + "message": "https://company.atlassian.net" + }, + "labelEmail": { + "message": "Email Address:" + }, + "labelApiToken": { + "message": "API Token:" + }, + "apiTokenHint": { + "message": "You can get your API token from $URL$.", + "placeholders": { + "url": { + "content": "$1" + } + } + }, + "labelProjectKey": { + "message": "Project Key:" + }, + "placeholderProjectKey": { + "message": "KEY" + }, + "labelIssueType": { + "message": "Issue Type:" + }, + "defaultIssueType": { + "message": "Bug" + }, + "btnSave": { + "message": "Save" + }, + "btnSaveTitle": { + "message": "Save Settings" + }, + "alertSaved": { + "message": "Saved" + }, + "errorMissingSettings": { + "message": "Settings are incomplete. Please fill in your Jira information through the settings page." + }, + "errorAlreadyRecording": { + "message": "Already recording." + }, + "errorCaptureTabFailed": { + "message": "Tab capture failed." + }, + "errorIssueCreateFailed": { + "message": "Issue create failed:" + }, + "errorAttachmentFailed": { + "message": "Attachment failed:" + }, + "adfHeadingAutoReport": { + "message": "Automated Report" + }, + "adfHeadingSummaryJson": { + "message": "Summary JSON" + }, + "adfSummaryUrl": { + "message": "URL:" + }, + "adfSummaryTime": { + "message": "Time:" + }, + "adfSummaryUa": { + "message": "UA:" + }, + "adfSummaryViewport": { + "message": "Viewport:" + }, + "adfSummaryErrorCount": { + "message": "Error count:" + }, + "adfSummaryConsoleCount": { + "message": "Console entries:" + }, + "adfSummaryNetworkCount": { + "message": "Network entries:" + }, + "adfSummaryResources": { + "message": "resources:" + }, + "adfSummaryCookies": { + "message": "Cookies:" + }, + "adfSummaryStorage": { + "message": "Storage:" + }, + "adfSummaryRecording": { + "message": "Screen recording:" + }, + "adfIncluded": { + "message": "included" + }, + "adfNotIncluded": { + "message": "not included" + }, + "adfFooterNote": { + "message": "See attached 'page-report.json' and 'screenshot.png' files for full details." + }, + "issueSummaryPrefix": { + "message": "[Bug]" + }, + "labelLanguage": { + "message": "Language:" + }, + "languageTurkish": { + "message": "Türkçe" + }, + "languageEnglish": { + "message": "English" + }, + "footerDisclaimer": { + "message": "Jira Bug Reporter is a third-party open-source tool that integrates with Jira, a product of Atlassian. It is not affiliated with, endorsed by, or approved by Atlassian. \"Atlassian\" and \"Jira\" are registered trademarks of Atlassian. The use of these names does not imply any endorsement or sponsorship." + }, + "footerContribute": { + "message": "and you can contribute to the project." + } +} diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json new file mode 100644 index 0000000..917a20e --- /dev/null +++ b/src/_locales/tr/messages.json @@ -0,0 +1,172 @@ +{ + "extName": { + "message": "Jira Bug Reporter" + }, + "extDescription": { + "message": "Aktif sayfadan hata ve ağ bilgilerini toplayıp Jira'da bilet açar." + }, + "popupTitle": { + "message": "Jira Bug Reporter" + }, + "popupSubtitle": { + "message": "Tek tıkla bu sayfanın ekran görüntüsü ve loglarını toplayıp Jira'da bilet açar." + }, + "labelCookies": { + "message": "Çerezler" + }, + "hintCookies": { + "message": "Cookies eklensin" + }, + "labelStorage": { + "message": "Depolama Özeti" + }, + "hintStorage": { + "message": "localStorage & sessionStorage özetleri eklensin" + }, + "btnCreateIssue": { + "message": "Jira'da bilet aç" + }, + "btnCreateIssueTitle": { + "message": "Jira'da yeni bilet aç" + }, + "statusCollecting": { + "message": "Bilgiler toplanıyor..." + }, + "statusCreated": { + "message": "Oluşturuldu:" + }, + "statusError": { + "message": "Hata:" + }, + "statusUnknown": { + "message": "Bilinmiyor" + }, + "optionsTitle": { + "message": "Jira Bug Reporter Ayarları" + }, + "optionsSubtitle": { + "message": "Uzantının çalışabilmesi için gerekli ayarları yapın." + }, + "labelJiraBaseUrl": { + "message": "Jira Sunucu Adresi:" + }, + "placeholderJiraBaseUrl": { + "message": "https://company.atlassian.net" + }, + "labelEmail": { + "message": "E-Posta Adresi:" + }, + "labelApiToken": { + "message": "API Belirteci:" + }, + "apiTokenHint": { + "message": "API Belirtecinizi $URL$ adresinden alabilirsiniz.", + "placeholders": { + "url": { + "content": "$1" + } + } + }, + "labelProjectKey": { + "message": "Proje Anahtarı:" + }, + "placeholderProjectKey": { + "message": "KEY" + }, + "labelIssueType": { + "message": "Issue Türü:" + }, + "defaultIssueType": { + "message": "Bug" + }, + "btnSave": { + "message": "Kaydet" + }, + "btnSaveTitle": { + "message": "Ayarları Kaydet" + }, + "alertSaved": { + "message": "Kaydedildi" + }, + "errorMissingSettings": { + "message": "Ayarlar eksik. Lütfen ayarlar sayfası üzerinden Jira bilgilerinizi doldurun." + }, + "errorAlreadyRecording": { + "message": "Zaten kayıt var." + }, + "errorCaptureTabFailed": { + "message": "Sekme yakalanamadı." + }, + "errorIssueCreateFailed": { + "message": "Issue create failed:" + }, + "errorAttachmentFailed": { + "message": "Attachment failed:" + }, + "adfHeadingAutoReport": { + "message": "Otomatik Rapor" + }, + "adfHeadingSummaryJson": { + "message": "Özet JSON" + }, + "adfSummaryUrl": { + "message": "URL:" + }, + "adfSummaryTime": { + "message": "Zaman:" + }, + "adfSummaryUa": { + "message": "UA:" + }, + "adfSummaryViewport": { + "message": "Viewport:" + }, + "adfSummaryErrorCount": { + "message": "Hata sayısı:" + }, + "adfSummaryConsoleCount": { + "message": "Console girdisi:" + }, + "adfSummaryNetworkCount": { + "message": "Network girdisi:" + }, + "adfSummaryResources": { + "message": "resources:" + }, + "adfSummaryCookies": { + "message": "Cookies:" + }, + "adfSummaryStorage": { + "message": "Storage:" + }, + "adfSummaryRecording": { + "message": "Ekran kaydı:" + }, + "adfIncluded": { + "message": "eklendi" + }, + "adfNotIncluded": { + "message": "yok" + }, + "adfFooterNote": { + "message": "Tam ayrıntılar için eklerdeki 'page-report.json' ve 'screenshot.png' dosyalarına bakınız." + }, + "issueSummaryPrefix": { + "message": "[Bug]" + }, + "labelLanguage": { + "message": "Dil:" + }, + "languageTurkish": { + "message": "Türkçe" + }, + "languageEnglish": { + "message": "English" + }, + "footerDisclaimer": { + "message": "Jira Bug Reporter, Atlassian'ın bir ürünü olan Jira ile entegrasyon sağlayan açık kaynak kodlu üçüncü taraf bir araçtır. Atlassian ile bağlantılı, ortak veya onaylı değildir. \"Atlassian\" ve \"Jira\", Atlassian'ın tescilli ticari markalarıdır. Bu adların kullanımı, herhangi bir destek veya sponsorluk anlamına gelmez." + }, + "footerContribute": { + "message": "üzerinden projeye siz de katkıda bulunabilirsiniz." + } +} diff --git a/src/background.js b/src/background.js index cb54b11..bde8a76 100644 --- a/src/background.js +++ b/src/background.js @@ -1,9 +1,14 @@ let rec = { mediaRecorder: null, chunks: [], stream: null }; +// Helper to get i18n message +function msg(key, substitutions) { + return chrome.i18n.getMessage(key, substitutions) || key; +} + async function getSettings() { const s = await chrome.storage.sync.get(["baseUrl","email","token","projectKey","issueType"]); if (!s.baseUrl || !s.email || !s.token || !s.projectKey || !s.issueType) { - throw new Error("Ayarlar eksik. Lütfen ayarlar sayfası üzerinden Jira bilgilerinizi doldurun."); + throw new Error(msg("errorMissingSettings")); } const auth = "Basic " + btoa(`${s.email}:${s.token}`); return { ...s, auth }; @@ -16,13 +21,13 @@ async function capturePng(tabId) { } async function recStart(tabId) { - if (rec.mediaRecorder) throw new Error("Zaten kayıt var."); + if (rec.mediaRecorder) throw new Error(msg("errorAlreadyRecording")); rec.stream = await chrome.tabCapture.capture({ audio: false, video: true, videoConstraints: { mandatory: { maxWidth: 1280, maxHeight: 720, maxFrameRate: 10 } } }); - if (!rec.stream) throw new Error("Sekme yakalanamadı."); + if (!rec.stream) throw new Error(msg("errorCaptureTabFailed")); rec.chunks = []; rec.mediaRecorder = new MediaRecorder(rec.stream, { mimeType: "video/webm;codecs=vp9" }); rec.mediaRecorder.ondataavailable = (e) => { if (e.data && e.data.size) rec.chunks.push(e.data); }; @@ -74,16 +79,16 @@ async function createIssue(settings, payload) { const host = new URL(url).hostname; const summaryItems = [ - `URL: ${url}`, - `Zaman: ${payload.meta.time}`, - `UA: ${payload.meta.userAgent}`, - `Viewport: ${payload.meta.viewport.w}x${payload.meta.viewport.h}`, - `Hata sayısı: ${payload.logs.errors.length}`, - `Console girdisi: ${payload.logs.console.length}`, - `Network girdisi: ${payload.logs.network.length} (resources: ${payload.resources.length})`, - `Cookies: ${payload.cookies ? "eklendi" : "yok"}`, - `Storage: ${payload.storage ? "eklendi" : "yok"}`, - `Ekran kaydı: ${payload._hasRecording ? "eklendi" : "yok"}` + `${msg("adfSummaryUrl")} ${url}`, + `${msg("adfSummaryTime")} ${payload.meta.time}`, + `${msg("adfSummaryUa")} ${payload.meta.userAgent}`, + `${msg("adfSummaryViewport")} ${payload.meta.viewport.w}x${payload.meta.viewport.h}`, + `${msg("adfSummaryErrorCount")} ${payload.logs.errors.length}`, + `${msg("adfSummaryConsoleCount")} ${payload.logs.console.length}`, + `${msg("adfSummaryNetworkCount")} ${payload.logs.network.length} (${msg("adfSummaryResources")} ${payload.resources.length})`, + `${msg("adfSummaryCookies")} ${payload.cookies ? msg("adfIncluded") : msg("adfNotIncluded")}`, + `${msg("adfSummaryStorage")} ${payload.storage ? msg("adfIncluded") : msg("adfNotIncluded")}`, + `${msg("adfSummaryRecording")} ${payload._hasRecording ? msg("adfIncluded") : msg("adfNotIncluded")}` ]; const previewJson = JSON.stringify( @@ -99,11 +104,11 @@ async function createIssue(settings, payload) { version: 1, type: "doc", content: [ - heading(3, "Otomatik Rapor"), + heading(3, msg("adfHeadingAutoReport")), bulletList(summaryItems), - heading(4, "Özet JSON"), + heading(4, msg("adfHeadingSummaryJson")), codeBlock("json", previewJson), - p("Tam ayrıntılar için eklerdeki 'page-report.json' ve 'screenshot.png' dosyalarına bakınız.") + p(msg("adfFooterNote")) ] }; @@ -111,7 +116,7 @@ async function createIssue(settings, payload) { fields: { project: { key: settings.projectKey }, issuetype: { name: settings.issueType }, - summary: `[Bug] ${payload.meta.title} @ ${host}`, + summary: `${msg("issueSummaryPrefix")} ${payload.meta.title} @ ${host}`, description: descriptionADF } }; @@ -126,7 +131,7 @@ async function createIssue(settings, payload) { body: JSON.stringify(body) }); - if (!r.ok) throw new Error(`Issue create failed: ${r.status} ${await r.text()}`); + if (!r.ok) throw new Error(`${msg("errorIssueCreateFailed")} ${r.status} ${await r.text()}`); return await r.json(); } @@ -144,7 +149,7 @@ async function uploadAttachment(settings, issueIdOrKey, files) { }, body: form }); - if (!r.ok) throw new Error(`Attachment failed: ${r.status} ${await r.text()}`); + if (!r.ok) throw new Error(`${msg("errorAttachmentFailed")} ${r.status} ${await r.text()}`); return await r.json(); } diff --git a/src/i18n.js b/src/i18n.js new file mode 100644 index 0000000..3b27a3d --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,66 @@ +// i18n helper utilities +const i18n = { + // Get translated message + getMessage: (key, substitutions) => { + return chrome.i18n.getMessage(key, substitutions) || key; + }, + + // Get current UI language + getUILanguage: () => { + return chrome.i18n.getUILanguage(); + }, + + // Get user's preferred language from storage or browser default + getPreferredLanguage: async () => { + const stored = await chrome.storage.sync.get(['language']); + if (stored.language) { + return stored.language; + } + // Default to browser language (tr or en) + const browserLang = chrome.i18n.getUILanguage(); + return browserLang.startsWith('tr') ? 'tr' : 'en'; + }, + + // Set user's preferred language + setPreferredLanguage: async (lang) => { + await chrome.storage.sync.set({ language: lang }); + }, + + // Translate all elements with data-i18n attribute + translatePage: () => { + document.querySelectorAll('[data-i18n]').forEach(el => { + const key = el.getAttribute('data-i18n'); + el.textContent = i18n.getMessage(key); + }); + + document.querySelectorAll('[data-i18n-placeholder]').forEach(el => { + const key = el.getAttribute('data-i18n-placeholder'); + el.placeholder = i18n.getMessage(key); + }); + + document.querySelectorAll('[data-i18n-title]').forEach(el => { + const key = el.getAttribute('data-i18n-title'); + el.title = i18n.getMessage(key); + }); + + document.querySelectorAll('[data-i18n-value]').forEach(el => { + const key = el.getAttribute('data-i18n-value'); + el.value = i18n.getMessage(key); + }); + + document.querySelectorAll('[data-i18n-html]').forEach(el => { + const key = el.getAttribute('data-i18n-html'); + const url = el.getAttribute('data-i18n-url'); + if (url) { + el.innerHTML = i18n.getMessage(key, url); + } else { + el.innerHTML = i18n.getMessage(key); + } + }); + } +}; + +// Export for use in other scripts +if (typeof module !== 'undefined' && module.exports) { + module.exports = i18n; +} diff --git a/src/manifest.json b/src/manifest.json index 20478b7..a4a9cbe 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,8 +1,9 @@ { "manifest_version": 3, - "name": "Jira Bug Reporter", + "name": "__MSG_extName__", "version": "1.1.3", - "description": "Aktif sayfadan hata ve ağ bilgilerini toplayıp Jira'da bilet açar.", + "description": "__MSG_extDescription__", + "default_locale": "tr", "icons": { "16": "icons/icon16.png", "32": "icons/icon32.png", diff --git a/src/options.html b/src/options.html index 7af8818..02989dd 100644 --- a/src/options.html +++ b/src/options.html @@ -4,6 +4,7 @@