diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 97100cc..1958b52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,6 +36,13 @@ jobs: - name: Fat-jar derle + test run: mvn -B -ntp clean verify + - name: Installer araclari (best-effort) + continue-on-error: true + shell: bash + run: | + if [ "${{ runner.os }}" = "Windows" ]; then choco install wixtoolset -y --no-progress || true; fi + if [ "${{ runner.os }}" = "Linux" ]; then sudo apt-get update && sudo apt-get install -y fakeroot || true; fi + - name: jpackage app-image (JRE gomulu) shell: bash run: | @@ -53,16 +60,48 @@ jobs: zip -r "../AutoClicker-${{ runner.os }}.zip" AutoClicker* >/dev/null fi - - name: Release'e OS paketini yukle - uses: softprops/action-gh-release@v2 - with: - prerelease: true - generate_release_notes: true - files: AutoClicker-${{ runner.os }}.zip + - name: jpackage native installer (msi/dmg/deb) - best-effort + id: installer + continue-on-error: true + shell: bash + run: | + case "${{ runner.os }}" in + Windows) T=msi ;; + macOS) T=dmg ;; + Linux) T=deb ;; + esac + jpackage --type "$T" --input target --main-jar autoclicker.jar \ + --name AutoClicker --app-version "${{ steps.ver.outputs.v }}" --dest installer \ + --java-options "-Dfile.encoding=UTF-8" - - name: Release'e fat-jar yukle (yalnizca Linux) - if: runner.os == 'Linux' + - name: Yayim dosyalari + SHA-256 checksum + shell: bash + run: | + mkdir -p out + cp "AutoClicker-${{ runner.os }}.zip" out/ 2>/dev/null || true + if [ -d installer ]; then cp installer/* out/ 2>/dev/null || true; fi + if [ "${{ runner.os }}" = "Linux" ]; then cp target/autoclicker.jar out/ 2>/dev/null || true; fi + cd out + for f in *; do + [ -f "$f" ] || continue + if command -v sha256sum >/dev/null 2>&1; then sha256sum "$f" > "$f.sha256"; else shasum -a 256 "$f" > "$f.sha256"; fi + done + ls -la + + # ---- KOD IMZALAMA (opsiyonel - SERTIFIKA gerektirir) ---- + # Ucretsiz yol: yukaridaki SHA-256 checksum'lar butunluk dogrulamasi saglar (imza degil). + # Imzalamak icin repo Settings > Secrets'a sertifikalari ekleyip asagidaki adimlari etkinlestirin: + # Windows: WIN_CERT_BASE64 (.pfx -> base64), WIN_CERT_PASSWORD + # - name: Windows imzala (jsign) + # if: runner.os == 'Windows' + # shell: bash + # run: | + # echo "${{ secrets.WIN_CERT_BASE64 }}" | base64 -d > cert.pfx + # for f in out/*.msi; do jsign --storetype PKCS12 --keystore cert.pfx --storepass "${{ secrets.WIN_CERT_PASSWORD }}" "$f"; done + # macOS: APPLE_DEV_ID sertifikasi import + codesign + 'xcrun notarytool submit --wait' ile imzala/notarize. + - name: Release'e yukle uses: softprops/action-gh-release@v2 with: prerelease: true - files: target/autoclicker.jar + generate_release_notes: true + files: out/* diff --git a/.gitignore b/.gitignore index 17a9e56..be215d3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,8 @@ dist/ *.properties # Maven wrapper properties'i *.properties'e ragmen izlenmeli !.mvn/wrapper/maven-wrapper.properties +# i18n ceviri kaynaklari *.properties'e ragmen izlenmeli +!src/main/resources/i18n/messages*.properties # ──────────────────────────────────────── # IDE dosyaları diff --git a/README.md b/README.md index d72dc95..6b6315d 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,26 @@ com.ohualtex.autoclicker --- +## 🔒 Güvenlik & Doğrulama + +Release dosyaları **kod imzalı değildir** (ücretsiz dağıtım); bunun yerine her dosya için **SHA-256 checksum** (`*.sha256`) yayınlanır. + +**Bütünlüğü doğrula:** +```bash +# Linux/macOS +shasum -a 256 -c AutoClicker-Linux.zip.sha256 +# Windows (PowerShell) +(Get-FileHash AutoClicker-Windows.zip -Algorithm SHA256).Hash +``` + +**İşletim sistemi uyarısını aşma** (imzasız olduğu için normaldir): +- **Windows SmartScreen:** *"Daha fazla bilgi" → "Yine de çalıştır"* +- **macOS Gatekeeper:** *Sistem Ayarları → Gizlilik ve Güvenlik → "Yine de Aç"*, veya `xattr -dr com.apple.quarantine AutoClicker.app` + +İlk çalıştırmada uygulama bir **sorumlu-kullanım onayı** ister ve **GitHub'dan yeni sürüm** olup olmadığını arka planda kontrol eder (config'de `checkUpdates=false` ile kapatılabilir). + +--- + ## 📌 Notlar & Uyumluluk - **Ayar konumu:** Ayarlar `%APPDATA%\AutoClicker\config.properties` (Windows) veya `~/.autoclicker/` (macOS/Linux) altında saklanır; installer ile kurulumda bile yazılabilir. diff --git a/pom.xml b/pom.xml index 21dd630..2b05531 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.ohualtex autoclicker - 7.5 + 8.5 jar AutoClicker Ultimate diff --git a/src/main/java/com/ohualtex/autoclicker/AutoClicker.java b/src/main/java/com/ohualtex/autoclicker/AutoClicker.java index 8f784d0..9b986d4 100644 --- a/src/main/java/com/ohualtex/autoclicker/AutoClicker.java +++ b/src/main/java/com/ohualtex/autoclicker/AutoClicker.java @@ -8,6 +8,7 @@ import com.ohualtex.autoclicker.model.ActionType; import com.ohualtex.autoclicker.model.MacroAction; import com.ohualtex.autoclicker.ui.Icons; +import com.ohualtex.autoclicker.ui.Widgets; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -149,6 +150,66 @@ public void windowClosing(WindowEvent e) { saveConfig(); } }); + + checkConsent(); // ilk calistirmada sorumlu-kullanim onayi (kabul edilmezse cikar) + checkForUpdate(); // arka planda surum kontrolu (best-effort, sessiz) + } + + // Ilk calistirmada sorumlu-kullanim/yasal sorumluluk reddi onayi; bir kez gosterilir + private void checkConsent() { + if (Boolean.parseBoolean(props.getProperty("consentAccepted", "false"))) return; + int r = JOptionPane.showConfirmDialog(this, Lang.get("consent_msg"), Lang.get("consent_title"), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + if (r != JOptionPane.OK_OPTION) { System.exit(0); } + props.setProperty("consentAccepted", "true"); + saveConfig(); + } + + // GitHub Releases'i kontrol eder; daha yeni surum varsa kullaniciya bildirir (arka plan, best-effort) + private void checkForUpdate() { + if (!Boolean.parseBoolean(props.getProperty("checkUpdates", "true"))) return; + Thread t = new Thread(() -> { + try { + java.net.http.HttpClient c = java.net.http.HttpClient.newHttpClient(); + java.net.http.HttpRequest req = java.net.http.HttpRequest.newBuilder() + .uri(java.net.URI.create("https://api.github.com/repos/Ohualtex/Auto_Clicker/releases/latest")) + .header("Accept", "application/vnd.github+json") + .timeout(java.time.Duration.ofSeconds(6)).build(); + String body = c.send(req, java.net.http.HttpResponse.BodyHandlers.ofString()).body(); + String tag = new com.google.gson.JsonParser().parse(body).getAsJsonObject().get("tag_name").getAsString(); + String latest = tag.startsWith("v") ? tag.substring(1) : tag; + if (isNewer(latest, appVersion())) { + SwingUtilities.invokeLater(() -> { + int r = JOptionPane.showConfirmDialog(this, + String.format(Lang.get("update_msg"), latest, appVersion()), + Lang.get("update_title"), JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE); + if (r == JOptionPane.YES_OPTION) openReleasesPage(); + }); + } + } catch (Exception ignore) { /* ag/parse hatasi: sessiz */ } + }, "AutoClicker-UpdateCheck"); + t.setDaemon(true); + t.start(); + } + + // "8.10" > "8.9" gibi sayisal-parcali surum karsilastirmasi + static boolean isNewer(String latest, String current) { + try { + String[] a = latest.split("\\."), b = current.split("\\."); + int n = Math.max(a.length, b.length); + for (int i = 0; i < n; i++) { + int x = i < a.length ? Integer.parseInt(a[i].trim()) : 0; + int y = i < b.length ? Integer.parseInt(b[i].trim()) : 0; + if (x != y) return x > y; + } + return false; + } catch (Exception e) { return false; } + } + + private void openReleasesPage() { + try { + Desktop.getDesktop().browse(java.net.URI.create("https://github.com/Ohualtex/Auto_Clicker/releases/latest")); + } catch (Exception ignore) {} } // Sistem tepsisi ikonu: simge durumuna kuculunce gizle, tray menusunden goster/baslat-durdur/cikis @@ -226,15 +287,7 @@ private void rebuildUI() { } private JButton createInfoButton(String tooltipKey) { - JButton btn = new JButton(new Icons.InfoIcon()); - btn.setMargin(new Insets(0,0,0,0)); - btn.setBorderPainted(false); - btn.setContentAreaFilled(false); - btn.setFocusPainted(false); - btn.setCursor(new Cursor(Cursor.HAND_CURSOR)); - btn.setToolTipText("

" + Lang.get(tooltipKey) + "

"); - btn.addActionListener(e -> JOptionPane.showMessageDialog(this, Lang.get(tooltipKey), Lang.get("info_title"), JOptionPane.INFORMATION_MESSAGE)); - return btn; + return Widgets.infoButton(this, tooltipKey); } private void applyColorsRecursively(Component c, Color color) { @@ -882,11 +935,14 @@ public void keyPressed(KeyEvent ev) { int t = typeBox.getSelectedIndex(); if(t==0) { int mIdx = mouseBox.getSelectedIndex(); - int mask = InputEvent.BUTTON1_DOWN_MASK; - if(mIdx==1) mask = InputEvent.BUTTON3_DOWN_MASK; - else if(mIdx==2) mask = InputEvent.BUTTON2_DOWN_MASK; - else if(mIdx==3) mask = MacroAction.DOUBLE_CLICK; - chainModel.addElement(new MacroAction(ActionType.MOUSE_CLICK, mask, 0)); + if (mIdx == 3) { + chainModel.addElement(new MacroAction(ActionType.MOUSE_DOUBLE_CLICK, 0, 0)); + } else { + int mask = InputEvent.BUTTON1_DOWN_MASK; + if(mIdx==1) mask = InputEvent.BUTTON3_DOWN_MASK; + else if(mIdx==2) mask = InputEvent.BUTTON2_DOWN_MASK; + chainModel.addElement(new MacroAction(ActionType.MOUSE_CLICK, mask, 0)); + } } else if(t==1) { chainModel.addElement(new MacroAction(ActionType.KEY_PRESS, selectedActKey[0], 0)); } else if(t==2) { @@ -1088,37 +1144,7 @@ private JPanel buildSettingsPanel() { } private JPanel createCpsPanel(String title, String defaultVal, int max, String infoKey, java.util.function.Consumer setSlider) { - JPanel panel = new JPanel(); - panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); - panel.setBorder(BorderFactory.createTitledBorder(title)); - - JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - JSlider slider = new JSlider(1, max, Integer.parseInt(defaultVal)); - JTextField field = new JTextField(defaultVal, 5); - - slider.addChangeListener(e -> field.setText(String.valueOf(slider.getValue()))); - field.addKeyListener(new KeyAdapter() { - public void keyReleased(KeyEvent e) { - boolean ok = false; - try { - int val = Integer.parseInt(field.getText().trim()); - if (val >= slider.getMinimum() && val <= max) { slider.setValue(val); ok = true; } - } catch (Exception ex) { } - // Bos alan notr; gecersiz/araliga sigmayan deger kirmizi cerceve - field.putClientProperty("JComponent.outline", (ok || field.getText().trim().isEmpty()) ? null : "error"); - field.repaint(); - } - }); - - setSlider.accept(slider); - - inputPanel.add(slider); - inputPanel.add(field); - if (infoKey != null) { - inputPanel.add(createInfoButton(infoKey)); - } - panel.add(inputPanel); - return panel; + return Widgets.cpsPanel(this, title, defaultVal, max, infoKey, setSlider); } private void initJNativeHook() { @@ -1437,13 +1463,12 @@ private boolean startChainMacro() { SwingUtilities.invokeLater(() -> chainList.setSelectedIndex(idx)); switch (action.type) { case MOUSE_CLICK: - if (action.p1 == MacroAction.DOUBLE_CLICK) { - doMouseClick(InputEvent.BUTTON1_DOWN_MASK); - robot.delay(40); - doMouseClick(InputEvent.BUTTON1_DOWN_MASK); - } else { - doMouseClick(action.p1); - } + doMouseClick(action.p1); + break; + case MOUSE_DOUBLE_CLICK: + doMouseClick(InputEvent.BUTTON1_DOWN_MASK); + robot.delay(40); + doMouseClick(InputEvent.BUTTON1_DOWN_MASK); break; case KEY_PRESS: robot.keyPress(action.p1); diff --git a/src/main/java/com/ohualtex/autoclicker/i18n/Lang.java b/src/main/java/com/ohualtex/autoclicker/i18n/Lang.java index 0e324a5..295696f 100644 --- a/src/main/java/com/ohualtex/autoclicker/i18n/Lang.java +++ b/src/main/java/com/ohualtex/autoclicker/i18n/Lang.java @@ -1,114 +1,28 @@ package com.ohualtex.autoclicker.i18n; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * 6 dilli yerellestirme. Metinler src/main/resources/i18n/messages_<lang>.properties + * dosyalarinda tutulur (UTF-8; Java 9+ ResourceBundle bunlari UTF-8 okur). + */ public final class Lang { - public static int L = 0; // 0=TR, 1=EN, 2=DE, 3=FR, 4=IT, 5=RU - static java.util.Map d = new java.util.HashMap<>(); - static { - d.put("st_idle", new String[]{"DURUM: BEKLIYOR", "STATUS: IDLE", "STATUS: BEREIT", "STATUT: EN ATTENTE", "STATO: IN ATTESA", "СТАТУС: ОЖИДАНИЕ"}); - d.put("st_run", new String[]{"DURUM: CALISIYOR", "STATUS: RUNNING", "STATUS: LÄUFT", "STATUT: EN COURS", "STATO: IN ESECUZIONE", "СТАТУС: РАБОТАЕТ"}); - d.put("st_lim", new String[]{"DURUM: LIMIT YENDI", "STATUS: LIMIT HIT", "STATUS: LIMIT ERREICHT", "STATUT: LIMITE", "STATO: LIMITE RAGGIUNTO", "СТАТУС: ЛИМИТ"}); - d.put("hook_fail", new String[]{"DURUM: KISAYOL DINLENEMIYOR", "STATUS: HOTKEY UNAVAILABLE", "STATUS: HOTKEY NICHT VERFUGBAR", "STATUT: RACCOURCI INDISPONIBLE", "STATO: HOTKEY NON DISPONIBILE", "СТАТУС: ГОРЯЧАЯ КЛАВИША НЕДОСТУПНА"}); - - d.put("t_mouse", new String[]{"Fare", "Mouse", "Maus", "Souris", "Mouse", "Мышь"}); - d.put("t_key", new String[]{"Klavye", "Keyboard", "Tastatur", "Clavier", "Tastiera", "Клавиатура"}); - d.put("t_chain", new String[]{"Zincir", "Chain", "Kette", "Chaîne", "Catena", "Цепь"}); - d.put("t_px", new String[]{"Piksel", "Pixel", "Pixel", "Pixel", "Pixel", "Пиксель"}); - d.put("t_set", new String[]{"Ayarlar", "Settings", "Einstellungen", "Paramètres", "Impostazioni", "Настройки"}); - - d.put("c_type", new String[]{"Tiklama Tipi: ", "Click Type: ", "Klick-Typ: ", "Type de clic: ", "Tipo di clic: ", "Тип клика: "}); - d.put("l_click", new String[]{"Sol Tik", "Left Click", "Linksklick", "Clic Gauche", "Clic Sinistro", "Левый Клик"}); - d.put("r_click", new String[]{"Sag Tik", "Right Click", "Rechtsklick", "Clic Droit", "Clic Destro", "Правый Клик"}); - d.put("m_click", new String[]{"Orta Tik", "Mid Click", "Mittelklick", "Clic Milieu", "Clic Centrale", "Средний Клик"}); - d.put("d_click", new String[]{"Cift Sol Tik", "Double Click", "Doppelklick", "Double Clic", "Doppio Clic", "Двойной Клик"}); - - d.put("fix_loc", new String[]{"Sabit Konuma Tikla", "Click Fixed Loc", "Feste Position", "Bouton Fixe", "Pos. Fissa", "Фикс. Место"}); - d.put("pick_loc", new String[]{"Konum Sec", "Pick Loc", "Wählen", "Choisir", "Seleziona", "Выбрать"}); - d.put("wait_mid", new String[]{"Bekliyor (Orta Tik)", "Waiting (Mid Click)", "Wartet (Mittelklick)", "Attente (Milieu)", "Attesa (Centrale)", "Ожидание (Ср. клик)"}); - - d.put("anti_ban", new String[]{"Insan Modu (Rastgele Gecikme)", "Humanizer (Random Delay)", "Menschenmodus (Zufall)", "Mode Humain (Délais Aléat.)", "Modalità Umana (Ritardo)", "Анти-Бан (случайная задержка)"}); - d.put("cps", new String[]{"Fare Hizi (CPS):", "Mouse Speed (CPS):", "Maus-Geschw. (CPS):", "Vitesse Souris (CPS):", "Velocità Mouse (CPS):", "Скорость Мыши (CPS):"}); - d.put("key_cps", new String[]{"Klavye Hizi (Saniyede):", "Key Speed (/sec):", "Tasten-Geschw. (/s):", "Vitesse Touche (/s):", "Velocità Tasto (/s):", "Скор. Клавиатуры (/с):"}); - - d.put("key_trg", new String[]{"Basilacak Tus:", "Target Key:", "Zieltaste:", "Touche Cible:", "Tasto Obiettivo:", "Целевая Клавиша:"}); - d.put("set_k", new String[]{"Tus Ata", "Set Key", "Setzen", "Définir", "Assegna", "Назначить"}); - d.put("press_k", new String[]{"Basin...", "Press...", "Drücken...", "Appuyez...", "Premi...", "Нажмите..."}); - - d.put("add_act", new String[]{"(+) Eylem Ekle", "(+) Add Action", "(+) Aktion", "(+) Ajouter", "(+) Aggiungi", "(+) Добавить"}); - d.put("del_act", new String[]{"(-) Secileni Sil", "(-) Remove", "(-) Löschen", "(-) Supprimer", "(-) Rimuovi", "(-) Удалить"}); - d.put("clr_act", new String[]{"(x) Temizle", "(x) Clear", "(x) Leeren", "(x) Effacer", "(x) Pulisci", "(x) Очистить"}); - - d.put("act_typ", new String[]{"Eylem Turu:", "Action Type:", "Aktionsart:", "Type d'action:", "Tipo di Azione:", "Тип Действия:"}); - d.put("ms_delay", new String[]{"Milisaniye (Gecikme):", "Delay (ms):", "Verzögerung (ms):", "Délai (ms):", "Ritardo (ms):", "Задержка (мс):"}); - d.put("list_add", new String[]{"[v] Listeye Ekle", "[v] Add to List", "[v] Hinzufügen", "[v] Ajouter", "[v] Aggiungi", "[v] Добавить"}); - d.put("tnt_mouse", new String[]{"Fare Tiklamasi", "Mouse Click", "Mausklick", "Clic Souris", "Clic Mouse", "Клик Мыши"}); - d.put("tnt_key", new String[]{"Klavye Tusu", "Key Press", "Taste Drücken", "Appui Touche", "Pressione Tasto", "Нажатие Клавиши"}); - d.put("tnt_move", new String[]{"Fareyi Tasi", "Move Mouse", "Maus Bewegen", "Bouger Souris", "Sposta Mouse", "Сдвинуть Мышь"}); - - d.put("px_1", new String[]{"1. Gozetlenecek Piksel", "1. Target Pixel", "1. Zielpixel", "1. Pixel Cible", "1. Pixel Obiettivo", "1. Цель Пиксель"}); - d.put("px_clr", new String[]{"Renk:", "Color:", "Farbe:", "Couleur:", "Colore:", "Цвет:"}); - d.put("px_2", new String[]{"2. Tetiklenme Sartlari", "2. Conditions", "2. Bedingungen", "2. Conditions", "2. Condizioni", "2. Условия"}); - d.put("px_cond1", new String[]{"Renk ESLESTIGINDE", "When Color MATCHES", "Farbe ÜBEREINSTIMMT", "Couleur CORRESPOND", "Colore CORRISPONDE", "Когда совпадает"}); - d.put("px_cond2", new String[]{"Renk DEGISTIGINDE", "When Color CHANGES", "Farbe WECHSELT", "Couleur CHANGE", "Colore CAMBIA", "Когда меняется"}); - d.put("px_tol", new String[]{"Tolerans Payi (%):", "Tolerance (%):", "Toleranz (%):", "Tolérance (%):", "Tolleranza (%):", "Допуск (%):"}); - d.put("px_3", new String[]{"3. Gerceklesecek Tepki", "3. Reaction Action", "3. Reaktion", "3. Réaction", "3. Reazione", "3. Реакция"}); - d.put("px_act3", new String[]{"Ozel Tusa Bas", "Press Custom Key", "Zieltaste drücken", "Appuyer Touche", "Premi Tasto", "Своя Клавиша"}); - d.put("px_act4", new String[]{"Sadece Zili Cal", "Only Beep", "Nur Piepen", "Bip Sonore", "Suona Solo", "Только Звук"}); - d.put("px_rate", new String[]{"Tarama Hizi (Ms):", "Scanner Rate (Ms):", "Scanrate (Ms):", "Vitesse Scan (Ms):", "Velocità Scan (Ms):", "Скорость (Мс):"}); - - d.put("set_hk", new String[]{"Kisayol Tusu (Baslat/Durdur):", "Start/Stop Hotkey:", "Start/Stopp Hotkey:", "Raccourci Start/Stop:", "Tasto Avvio/Arresto:", "Горячая Клавиша:"}); - d.put("lim_title", new String[]{"Otomatik Durdurma Sinirlari", "Auto Stop Limiters", "Auto-Stopp Begrenzungen", "Limites Arrêt Auto", "Limiti Arresto Auto", "Лимиты Автоостановки"}); - d.put("lim_use", new String[]{"Limitoru Aktif Et", "Enable Limiter", "Limiter aktivieren", "Activer Limiteur", "Abilita Limitatore", "Вкл. Лимитер"}); - d.put("lim_after", new String[]{"Sinir:", "Limit:", "Limit:", "Dans:", "Dopo:", "Лимит:"}); - d.put("lim_min", new String[]{"Dakika Sonra", "Minutes Later", "Minuten", "Minutes", "Minuti", "Минут"}); - d.put("lim_iter", new String[]{"Dongu Sonra", "Iterations Later", "Iterationen", "Itérations", "Iterazioni", "Итераций"}); - d.put("lim_stop", new String[]{"Sadece Makroyu Durdur", "Just Stop Macro", "Makro stoppen", "Arrêter Macro", "Ferma Macro", "Только стоп"}); - d.put("lim_shut", new String[]{"Bilgisayari Kapat", "Shutdown PC", "PC herunterfahren", "Éteindre le PC", "Spegni PC", "Выкл ПК"}); - - d.put("style", new String[]{"(O) Gorunum Ozellestirme", "(O) UI Customization", "(O) UI Anpassung", "(O) Apparence UI", "(O) Aspetto UI", "(O) Дизайн UI"}); - d.put("theme", new String[]{"Ana Tema:", "Main Theme:", "Hauptthema:", "Thème:", "Tema Principale:", "Тема:"}); - d.put("theme_d", new String[]{"Karanlik Mod", "Dark Mode", "Dunkler Modus", "Sombre", "Modo Scuro", "Темный"}); - d.put("theme_l", new String[]{"Aydinlik Mod", "Light Mode", "Heller Modus", "Clair", "Modo Chiaro", "Светлый"}); - d.put("txt_size", new String[]{"Yazi Boyutu:", "Text Size:", "Textgröße:", "Taille:", "Dimensione:", "Размер:"}); - d.put("txt_col", new String[]{"Metin Rengi:", "Text Color:", "Textfarbe:", "Couleur Texte:", "Colore Testo:", "Цвет Текста:"}); - d.put("col_pick", new String[]{"Renk Sec", "Pick Color", "Farbe", "Choisir", "Seleziona", "Выбрать Цвет"}); - d.put("apply_s", new String[]{"Uygula/Kaydet", "Apply/Save", "Anwenden", "Appliquer", "Applica", "Применить"}); - d.put("lang_title", new String[]{"(L) Dil (Language)", "(L) Language", "(L) Sprache", "(L) Langue", "(L) Lingua", "(L) Язык"}); - - d.put("info_hum", new String[]{"Anti-Ban: Robotik tiklamalari saptirmak icin gecikmelere minik sapmalar ekler.", "Anti-Ban: Adds random fluctuations to delays to simulate human behavior and evade detection.", "Anti-Ban: Fügt den Verzögerungen zufällige Schwankungen hinzu.", "Anti-Ban: Ajoute des fluctuations aléatoires aux délais.", "Anti-Ban: Aggiunge fluttuazioni casuali ai ritardi.", "Анти-Бан: Добавляет случайные колебания к задержкам."}); - d.put("info_lim", new String[]{"Otomasyonu belirli bir sure sonra kapatir.", "Stops the automation automatically after a set time or cycle count. Ideal for AFK macros.", "Stoppt die Automatisierung automatisch nach einer Weile.", "Arrête automatiquement l'automatisation.", "Ferma l'automazione in base a limiti.", "Останавливает автоматизацию при достижении лимита."}); - d.put("info_px", new String[]{"Renk Toleransi: Ufak golge farkliliklarinin renk algisini bozmasini engeller.", "Color Tolerance: Prevents minor in-game shading/lighting shifts from ruining detection.", "Farbtoleranz: Verhindert kleine Schattenfehler.", "Tolérance: Empêche les petits changements de lumière de fausser la détection.", "Tolleranza: Evita che l'illuminazione rompa l'algoritmo.", "Допуск: Игнорирует изменения освещения."}); - d.put("info_cps", new String[]{"Saniyedeki tiklama hizini belirler (Click Per Second).", "Sets the click speed per second (CPS).", "Legt die Klicks pro Sekunde fest.", "Définit la vitesse de clic (CPS).", "Imposta i clic al secondo (CPS).", "Устанавливает кликов в секунду (CPS)."}); - d.put("info_px_cond", new String[]{"Eslestiginde: Renk gorundugunde tepki verir.\nDegistiginde: Renk kayboldugunda tepki verir.", "Matches: Reacts when color appears.\nChanges: Reacts when color disappears.", "Stimmt überein: Reagiert, wenn die Farbe erscheint.", "Correspond: Agit quand la couleur apparait.", "Corrisponde: Agisce quando il colore appare.", "Совпадает: Реагирует на появление цвета."}); - d.put("info_px_rate", new String[]{"Tarama Hizi: Ekranin ne siklikla kontrol edilecegini belirler (Milisaniye).", "Scan Rate: How often to check the screen (Milliseconds).", "Scanrate: Wie oft der Bildschirm überprüft wird (ms).", "Taux de scan: Fréquence de vérification (ms).", "Velocità scan: Frequenza di controllo schermo (ms).", "Скорость: Частота проверки экрана (мс)."}); - d.put("info_title", new String[]{"Bilgi", "Info", "Info", "Info", "Info", "Информация"}); - d.put("reset", new String[]{"Sifirla", "Reset", "Zurücksetzen", "Réinit.", "Reimposta", "Сброс"}); - d.put("shut_ok", new String[]{"Limit asildi! Bilgisayar %s icinde kapatilacak.", "Limit reached! PC will shut down in %s.", "Limit erreicht! PC fährt in %s herunter.", "Limite atteinte! Le PC s'eteindra dans %s.", "Limite raggiunto! Il PC si spegnera tra %s.", "Лимит достигнут! ПК выключится через %s."}); - d.put("shut_title", new String[]{"Bilgisayar Kapatiliyor", "Shutting Down", "Herunterfahren", "Arret du PC", "Spegnimento PC", "Выключение ПК"}); - d.put("shut_pending", new String[]{"Limit asildi! Bilgisayar %s icinde kapanacak.\nVazgecmek icin asagidaki butona basin.", "Limit reached! PC shuts down in %s.\nPress the button below to cancel.", "Limit erreicht! PC fährt in %s herunter.\nZum Abbrechen unten klicken.", "Limite atteinte! Arret dans %s.\nCliquez ci-dessous pour annuler.", "Limite raggiunto! Spegnimento tra %s.\nPremi sotto per annullare.", "Лимит достигнут! Выключение через %s.\nНажмите ниже для отмены."}); - d.put("shut_cancel_btn", new String[]{"Kapatmayi Iptal Et", "Cancel Shutdown", "Abbrechen", "Annuler l'arret", "Annulla spegnimento", "Отменить выключение"}); - d.put("shut_cancelled", new String[]{"Bilgisayar kapatma iptal edildi.", "Shutdown cancelled.", "Herunterfahren abgebrochen.", "Arret annule.", "Spegnimento annullato.", "Выключение отменено."}); - d.put("lim_invalid", new String[]{"DURUM: GECERSIZ LIMIT DEGERI", "STATUS: INVALID LIMIT VALUE", "STATUS: UNGULTIGER LIMITWERT", "STATUT: LIMITE INVALIDE", "STATO: LIMITE NON VALIDO", "СТАТУС: НЕВЕРНЫЙ ЛИМИТ"}); - d.put("sched_title", new String[]{"(Z) Zamanlayici", "(Z) Scheduler", "(Z) Zeitplaner", "(Z) Planificateur", "(Z) Pianificatore", "(Z) Планировщик"}); - d.put("sched_use", new String[]{"Gecikmeli baslat", "Delayed start", "Verzögerter Start", "Démarrage différé", "Avvio ritardato", "Запуск с задержкой"}); - d.put("sched_sec", new String[]{"saniye sonra", "seconds later", "Sekunden später", "secondes après", "secondi dopo", "секунд спустя"}); - d.put("sched_countdown", new String[]{"DURUM: BASLIYOR (%d sn)", "STATUS: STARTING (%d s)", "STATUS: START IN (%d s)", "STATUT: DEBUT (%d s)", "STATO: AVVIO (%d s)", "СТАТУС: СТАРТ (%d с)"}); - d.put("tray_show", new String[]{"Goster", "Show", "Anzeigen", "Afficher", "Mostra", "Показать"}); - d.put("tray_toggle", new String[]{"Baslat / Durdur", "Start / Stop", "Start / Stopp", "Demarrer / Arreter", "Avvia / Ferma", "Старт / Стоп"}); - d.put("tray_exit", new String[]{"Cikis", "Exit", "Beenden", "Quitter", "Esci", "Выход"}); - d.put("tabhk_title", new String[]{"(K) Sekme Kisayollari", "(K) Per-Tab Hotkeys", "(K) Tab-Hotkeys", "(K) Raccourcis par onglet", "(K) Tasti per scheda", "(K) Горячие клавиши вкладок"}); - d.put("io_export", new String[]{"Disa Aktar (JSON)", "Export (JSON)", "Export (JSON)", "Exporter (JSON)", "Esporta (JSON)", "Экспорт (JSON)"}); - d.put("io_import", new String[]{"Ice Aktar (JSON)", "Import (JSON)", "Import (JSON)", "Importer (JSON)", "Importa (JSON)", "Импорт (JSON)"}); - d.put("io_fail", new String[]{"Dosya islemi basarisiz: ", "File operation failed: ", "Dateioperation fehlgeschlagen: ", "Echec du fichier: ", "Operazione file fallita: ", "Сбой работы с файлом: "}); - d.put("prof_title", new String[]{"(P) Profiller", "(P) Profiles", "(P) Profile", "(P) Profils", "(P) Profili", "(P) Профили"}); - d.put("prof_save", new String[]{"Kaydet", "Save", "Speichern", "Enregistrer", "Salva", "Сохранить"}); - d.put("prof_load", new String[]{"Yukle", "Load", "Laden", "Charger", "Carica", "Загрузить"}); - d.put("prof_del", new String[]{"Sil", "Delete", "Löschen", "Supprimer", "Elimina", "Удалить"}); - d.put("rec_start", new String[]{"● Kaydet", "● Record", "● Aufnehmen", "● Enregistrer", "● Registra", "● Запись"}); - d.put("rec_stop", new String[]{"■ Durdur (ESC)", "■ Stop (ESC)", "■ Stopp (ESC)", "■ Arreter (ESC)", "■ Ferma (ESC)", "■ Стоп (ESC)"}); - d.put("rec_status", new String[]{"DURUM: KAYDEDILIYOR (ESC=dur)", "STATUS: RECORDING (ESC=stop)", "STATUS: AUFNAHME (ESC=stopp)", "STATUT: ENREGISTREMENT (ESC)", "STATO: REGISTRAZIONE (ESC)", "СТАТУС: ЗАПИСЬ (ESC=стоп)"}); - d.put("shut_fail", new String[]{"Kapatma komutu basarisiz oldu: ", "Shutdown command failed: ", "Herunterfahren fehlgeschlagen: ", "Echec de la commande d'arret: ", "Comando di spegnimento fallito: ", "Сбой команды выключения: "}); - d.put("shut_unsup", new String[]{"Bu isletim sisteminde otomatik kapatma desteklenmiyor: ", "Automatic shutdown not supported on this OS: ", "Automatisches Herunterfahren auf diesem OS nicht unterstützt: ", "Arret automatique non supporte sur cet OS: ", "Spegnimento automatico non supportato su questo OS: ", "Автовыключение не поддерживается в этой ОС: "}); + private Lang() {} + + // 0=TR 1=EN 2=DE 3=FR 4=IT 5=RU + private static final String[] LOCALES = {"tr", "en", "de", "fr", "it", "ru"}; + + /** Aktif dil indeksi (langBox tarafindan ayarlanir). */ + public static int L = 0; + + public static String get(String key) { + try { + int idx = (L >= 0 && L < LOCALES.length) ? L : 0; + return ResourceBundle.getBundle("i18n.messages", new Locale(LOCALES[idx])).getString(key); + } catch (Exception e) { + return key; // anahtar/dosya bulunamazsa anahtarin kendisi (eski davranis) + } } - public static String get(String key) { return d.containsKey(key) ? d.get(key)[L] : key; } } diff --git a/src/main/java/com/ohualtex/autoclicker/model/ActionType.java b/src/main/java/com/ohualtex/autoclicker/model/ActionType.java index c92a5da..bcee4cc 100644 --- a/src/main/java/com/ohualtex/autoclicker/model/ActionType.java +++ b/src/main/java/com/ohualtex/autoclicker/model/ActionType.java @@ -1,4 +1,4 @@ package com.ohualtex.autoclicker.model; /** Zincir makro adim turleri. */ -public enum ActionType { MOUSE_CLICK, KEY_PRESS, MOUSE_MOVE, DELAY } +public enum ActionType { MOUSE_CLICK, MOUSE_DOUBLE_CLICK, KEY_PRESS, MOUSE_MOVE, DELAY } diff --git a/src/main/java/com/ohualtex/autoclicker/model/MacroAction.java b/src/main/java/com/ohualtex/autoclicker/model/MacroAction.java index deee6d1..9e5c5dd 100644 --- a/src/main/java/com/ohualtex/autoclicker/model/MacroAction.java +++ b/src/main/java/com/ohualtex/autoclicker/model/MacroAction.java @@ -7,8 +7,8 @@ /** Zincir makrosundaki tek bir adim (tur + iki parametre). Listede toString ile gosterilir, serialize ile saklanir. */ public class MacroAction { - /** MOUSE_CLICK adiminda p1 bu degerse "cift sol tik" demektir (sentinel/isaret degeri). */ - public static final int DOUBLE_CLICK = 999; + // Eski configlerde cift-tik "MOUSE_CLICK:999" olarak saklaniyordu; geriye uyum icin tutulur. + private static final int LEGACY_DOUBLE_CLICK = 999; public ActionType type; public int p1, p2; @@ -17,7 +17,8 @@ public class MacroAction { public String toString() { switch(type) { - case MOUSE_CLICK: return Lang.get("tnt_mouse") + ": " + (p1==InputEvent.BUTTON1_DOWN_MASK ? Lang.get("l_click") : p1==InputEvent.BUTTON3_DOWN_MASK ? Lang.get("r_click") : p1==DOUBLE_CLICK ? Lang.get("d_click") : Lang.get("m_click")); + case MOUSE_CLICK: return Lang.get("tnt_mouse") + ": " + (p1==InputEvent.BUTTON1_DOWN_MASK ? Lang.get("l_click") : p1==InputEvent.BUTTON3_DOWN_MASK ? Lang.get("r_click") : Lang.get("m_click")); + case MOUSE_DOUBLE_CLICK: return Lang.get("tnt_mouse") + ": " + Lang.get("d_click"); case KEY_PRESS: return Lang.get("tnt_key") + ": " + KeyEvent.getKeyText(p1); case MOUSE_MOVE: return Lang.get("tnt_move") + ": X=" + p1 + ", Y=" + p2; case DELAY: return Lang.get("ms_delay") + " " + p1; @@ -29,6 +30,13 @@ public String toString() { public static MacroAction deserialize(String s) { String[] parts = s.split(":"); - return new MacroAction(ActionType.valueOf(parts[0]), Integer.parseInt(parts[1]), Integer.parseInt(parts[2])); + ActionType t = ActionType.valueOf(parts[0]); + int p1 = Integer.parseInt(parts[1]); + int p2 = Integer.parseInt(parts[2]); + // Geriye uyum: eski "MOUSE_CLICK:999" -> MOUSE_DOUBLE_CLICK + if (t == ActionType.MOUSE_CLICK && p1 == LEGACY_DOUBLE_CLICK) { + return new MacroAction(ActionType.MOUSE_DOUBLE_CLICK, 0, 0); + } + return new MacroAction(t, p1, p2); } } diff --git a/src/main/java/com/ohualtex/autoclicker/ui/Widgets.java b/src/main/java/com/ohualtex/autoclicker/ui/Widgets.java new file mode 100644 index 0000000..601178e --- /dev/null +++ b/src/main/java/com/ohualtex/autoclicker/ui/Widgets.java @@ -0,0 +1,60 @@ +package com.ohualtex.autoclicker.ui; + +import com.ohualtex.autoclicker.i18n.Lang; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.function.Consumer; + +/** Durum-bagimsiz, yeniden kullanilabilir Swing widget kuruculari (bilgi butonu, CPS/sayisal panel). */ +public final class Widgets { + + private Widgets() {} + + /** Yaninda info ikonu olan, tiklayinca aciklama gosteren buton. */ + public static JButton infoButton(Component parent, String tooltipKey) { + JButton btn = new JButton(new Icons.InfoIcon()); + btn.setMargin(new Insets(0, 0, 0, 0)); + btn.setBorderPainted(false); + btn.setContentAreaFilled(false); + btn.setFocusPainted(false); + btn.setCursor(new Cursor(Cursor.HAND_CURSOR)); + btn.setToolTipText("

" + Lang.get(tooltipKey) + "

"); + btn.addActionListener(e -> JOptionPane.showMessageDialog(parent, Lang.get(tooltipKey), Lang.get("info_title"), JOptionPane.INFORMATION_MESSAGE)); + return btn; + } + + /** Baslikli kenarlik + slider + senkron metin alani (gecersiz girdide kirmizi cerceve) + opsiyonel info butonu. */ + public static JPanel cpsPanel(Component parent, String title, String defaultVal, int max, String infoKey, + Consumer setSlider) { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setBorder(BorderFactory.createTitledBorder(title)); + + JPanel inputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JSlider slider = new JSlider(1, max, Integer.parseInt(defaultVal)); + JTextField field = new JTextField(defaultVal, 5); + + slider.addChangeListener(e -> field.setText(String.valueOf(slider.getValue()))); + field.addKeyListener(new KeyAdapter() { + public void keyReleased(KeyEvent e) { + boolean ok = false; + try { + int val = Integer.parseInt(field.getText().trim()); + if (val >= slider.getMinimum() && val <= max) { slider.setValue(val); ok = true; } + } catch (Exception ex) { } + field.putClientProperty("JComponent.outline", (ok || field.getText().trim().isEmpty()) ? null : "error"); + field.repaint(); + } + }); + + setSlider.accept(slider); + inputPanel.add(slider); + inputPanel.add(field); + if (infoKey != null) inputPanel.add(infoButton(parent, infoKey)); + panel.add(inputPanel); + return panel; + } +} diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..a3d2b60 --- /dev/null +++ b/src/main/resources/i18n/messages.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - default(en) +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Action Type\: +add_act=(+) Add Action +anti_ban=Humanizer (Random Delay) +apply_s=Apply/Save +c_type=Click Type\: +clr_act=(x) Clear +col_pick=Pick Color +cps=Mouse Speed (CPS)\: +d_click=Double Click +del_act=(-) Remove +fix_loc=Click Fixed Loc +hook_fail=STATUS\: HOTKEY UNAVAILABLE +info_cps=Sets the click speed per second (CPS). +info_hum=Anti-Ban\: Adds random fluctuations to delays to simulate human behavior and evade detection. +info_lim=Stops the automation automatically after a set time or cycle count. Ideal for AFK macros. +info_px=Color Tolerance\: Prevents minor in-game shading/lighting shifts from ruining detection. +info_px_cond=Matches\: Reacts when color appears.\nChanges\: Reacts when color disappears. +info_px_rate=Scan Rate\: How often to check the screen (Milliseconds). +info_title=Info +io_export=Export (JSON) +io_fail=File operation failed\: +io_import=Import (JSON) +key_cps=Key Speed (/sec)\: +key_trg=Target Key\: +l_click=Left Click +lang_title=(L) Language +lim_after=Limit\: +lim_invalid=STATUS\: INVALID LIMIT VALUE +lim_iter=Iterations Later +lim_min=Minutes Later +lim_shut=Shutdown PC +lim_stop=Just Stop Macro +lim_title=Auto Stop Limiters +lim_use=Enable Limiter +list_add=[v] Add to List +m_click=Mid Click +ms_delay=Delay (ms)\: +pick_loc=Pick Loc +press_k=Press... +prof_del=Delete +prof_load=Load +prof_save=Save +prof_title=(P) Profiles +px_1=1. Target Pixel +px_2=2. Conditions +px_3=3. Reaction Action +px_act3=Press Custom Key +px_act4=Only Beep +px_clr=Color\: +px_cond1=When Color MATCHES +px_cond2=When Color CHANGES +px_rate=Scanner Rate (Ms)\: +px_tol=Tolerance (%)\: +r_click=Right Click +rec_start=● Record +rec_status=STATUS\: RECORDING (ESC\=stop) +rec_stop=■ Stop (ESC) +reset=Reset +sched_countdown=STATUS\: STARTING (%d s) +sched_sec=seconds later +sched_title=(Z) Scheduler +sched_use=Delayed start +set_hk=Start/Stop Hotkey\: +set_k=Set Key +shut_cancel_btn=Cancel Shutdown +shut_cancelled=Shutdown cancelled. +shut_fail=Shutdown command failed\: +shut_ok=Limit reached\! PC will shut down in %s. +shut_pending=Limit reached\! PC shuts down in %s.\nPress the button below to cancel. +shut_title=Shutting Down +shut_unsup=Automatic shutdown not supported on this OS\: +st_idle=STATUS\: IDLE +st_lim=STATUS\: LIMIT HIT +st_run=STATUS\: RUNNING +style=(O) UI Customization +t_chain=Chain +t_key=Keyboard +t_mouse=Mouse +t_px=Pixel +t_set=Settings +tabhk_title=(K) Per-Tab Hotkeys +theme=Main Theme\: +theme_d=Dark Mode +theme_l=Light Mode +tnt_key=Key Press +tnt_mouse=Mouse Click +tnt_move=Move Mouse +tray_exit=Exit +tray_show=Show +tray_toggle=Start / Stop +txt_col=Text Color\: +txt_size=Text Size\: +wait_mid=Waiting (Mid Click) + +consent_title=Responsible Use Agreement +consent_msg=This tool is for learning and productivity. Using it in anti-cheat protected games risks account bans; you are responsible. Do you accept? +update_title=Update Available +update_msg=New version %s is available (you have %s). Open the download page? diff --git a/src/main/resources/i18n/messages_de.properties b/src/main/resources/i18n/messages_de.properties new file mode 100644 index 0000000..16ab18d --- /dev/null +++ b/src/main/resources/i18n/messages_de.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - de +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Aktionsart\: +add_act=(+) Aktion +anti_ban=Menschenmodus (Zufall) +apply_s=Anwenden +c_type=Klick-Typ\: +clr_act=(x) Leeren +col_pick=Farbe +cps=Maus-Geschw. (CPS)\: +d_click=Doppelklick +del_act=(-) Löschen +fix_loc=Feste Position +hook_fail=STATUS\: HOTKEY NICHT VERFUGBAR +info_cps=Legt die Klicks pro Sekunde fest. +info_hum=Anti-Ban\: Fügt den Verzögerungen zufällige Schwankungen hinzu. +info_lim=Stoppt die Automatisierung automatisch nach einer Weile. +info_px=Farbtoleranz\: Verhindert kleine Schattenfehler. +info_px_cond=Stimmt überein\: Reagiert, wenn die Farbe erscheint. +info_px_rate=Scanrate\: Wie oft der Bildschirm überprüft wird (ms). +info_title=Info +io_export=Export (JSON) +io_fail=Dateioperation fehlgeschlagen\: +io_import=Import (JSON) +key_cps=Tasten-Geschw. (/s)\: +key_trg=Zieltaste\: +l_click=Linksklick +lang_title=(L) Sprache +lim_after=Limit\: +lim_invalid=STATUS\: UNGULTIGER LIMITWERT +lim_iter=Iterationen +lim_min=Minuten +lim_shut=PC herunterfahren +lim_stop=Makro stoppen +lim_title=Auto-Stopp Begrenzungen +lim_use=Limiter aktivieren +list_add=[v] Hinzufügen +m_click=Mittelklick +ms_delay=Verzögerung (ms)\: +pick_loc=Wählen +press_k=Drücken... +prof_del=Löschen +prof_load=Laden +prof_save=Speichern +prof_title=(P) Profile +px_1=1. Zielpixel +px_2=2. Bedingungen +px_3=3. Reaktion +px_act3=Zieltaste drücken +px_act4=Nur Piepen +px_clr=Farbe\: +px_cond1=Farbe ÜBEREINSTIMMT +px_cond2=Farbe WECHSELT +px_rate=Scanrate (Ms)\: +px_tol=Toleranz (%)\: +r_click=Rechtsklick +rec_start=● Aufnehmen +rec_status=STATUS\: AUFNAHME (ESC\=stopp) +rec_stop=■ Stopp (ESC) +reset=Zurücksetzen +sched_countdown=STATUS\: START IN (%d s) +sched_sec=Sekunden später +sched_title=(Z) Zeitplaner +sched_use=Verzögerter Start +set_hk=Start/Stopp Hotkey\: +set_k=Setzen +shut_cancel_btn=Abbrechen +shut_cancelled=Herunterfahren abgebrochen. +shut_fail=Herunterfahren fehlgeschlagen\: +shut_ok=Limit erreicht\! PC fährt in %s herunter. +shut_pending=Limit erreicht\! PC fährt in %s herunter.\nZum Abbrechen unten klicken. +shut_title=Herunterfahren +shut_unsup=Automatisches Herunterfahren auf diesem OS nicht unterstützt\: +st_idle=STATUS\: BEREIT +st_lim=STATUS\: LIMIT ERREICHT +st_run=STATUS\: LÄUFT +style=(O) UI Anpassung +t_chain=Kette +t_key=Tastatur +t_mouse=Maus +t_px=Pixel +t_set=Einstellungen +tabhk_title=(K) Tab-Hotkeys +theme=Hauptthema\: +theme_d=Dunkler Modus +theme_l=Heller Modus +tnt_key=Taste Drücken +tnt_mouse=Mausklick +tnt_move=Maus Bewegen +tray_exit=Beenden +tray_show=Anzeigen +tray_toggle=Start / Stopp +txt_col=Textfarbe\: +txt_size=Textgröße\: +wait_mid=Wartet (Mittelklick) + +consent_title=Nutzungsvereinbarung +consent_msg=Dieses Tool dient dem Lernen und der Produktivitaet. Nutzung in Anti-Cheat-Spielen birgt Sperrrisiken; Sie sind verantwortlich. Akzeptieren Sie? +update_title=Update verfuegbar +update_msg=Neue Version %s verfuegbar (aktuell %s). Download-Seite oeffnen? diff --git a/src/main/resources/i18n/messages_en.properties b/src/main/resources/i18n/messages_en.properties new file mode 100644 index 0000000..1a9c64a --- /dev/null +++ b/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - en +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Action Type\: +add_act=(+) Add Action +anti_ban=Humanizer (Random Delay) +apply_s=Apply/Save +c_type=Click Type\: +clr_act=(x) Clear +col_pick=Pick Color +cps=Mouse Speed (CPS)\: +d_click=Double Click +del_act=(-) Remove +fix_loc=Click Fixed Loc +hook_fail=STATUS\: HOTKEY UNAVAILABLE +info_cps=Sets the click speed per second (CPS). +info_hum=Anti-Ban\: Adds random fluctuations to delays to simulate human behavior and evade detection. +info_lim=Stops the automation automatically after a set time or cycle count. Ideal for AFK macros. +info_px=Color Tolerance\: Prevents minor in-game shading/lighting shifts from ruining detection. +info_px_cond=Matches\: Reacts when color appears.\nChanges\: Reacts when color disappears. +info_px_rate=Scan Rate\: How often to check the screen (Milliseconds). +info_title=Info +io_export=Export (JSON) +io_fail=File operation failed\: +io_import=Import (JSON) +key_cps=Key Speed (/sec)\: +key_trg=Target Key\: +l_click=Left Click +lang_title=(L) Language +lim_after=Limit\: +lim_invalid=STATUS\: INVALID LIMIT VALUE +lim_iter=Iterations Later +lim_min=Minutes Later +lim_shut=Shutdown PC +lim_stop=Just Stop Macro +lim_title=Auto Stop Limiters +lim_use=Enable Limiter +list_add=[v] Add to List +m_click=Mid Click +ms_delay=Delay (ms)\: +pick_loc=Pick Loc +press_k=Press... +prof_del=Delete +prof_load=Load +prof_save=Save +prof_title=(P) Profiles +px_1=1. Target Pixel +px_2=2. Conditions +px_3=3. Reaction Action +px_act3=Press Custom Key +px_act4=Only Beep +px_clr=Color\: +px_cond1=When Color MATCHES +px_cond2=When Color CHANGES +px_rate=Scanner Rate (Ms)\: +px_tol=Tolerance (%)\: +r_click=Right Click +rec_start=● Record +rec_status=STATUS\: RECORDING (ESC\=stop) +rec_stop=■ Stop (ESC) +reset=Reset +sched_countdown=STATUS\: STARTING (%d s) +sched_sec=seconds later +sched_title=(Z) Scheduler +sched_use=Delayed start +set_hk=Start/Stop Hotkey\: +set_k=Set Key +shut_cancel_btn=Cancel Shutdown +shut_cancelled=Shutdown cancelled. +shut_fail=Shutdown command failed\: +shut_ok=Limit reached\! PC will shut down in %s. +shut_pending=Limit reached\! PC shuts down in %s.\nPress the button below to cancel. +shut_title=Shutting Down +shut_unsup=Automatic shutdown not supported on this OS\: +st_idle=STATUS\: IDLE +st_lim=STATUS\: LIMIT HIT +st_run=STATUS\: RUNNING +style=(O) UI Customization +t_chain=Chain +t_key=Keyboard +t_mouse=Mouse +t_px=Pixel +t_set=Settings +tabhk_title=(K) Per-Tab Hotkeys +theme=Main Theme\: +theme_d=Dark Mode +theme_l=Light Mode +tnt_key=Key Press +tnt_mouse=Mouse Click +tnt_move=Move Mouse +tray_exit=Exit +tray_show=Show +tray_toggle=Start / Stop +txt_col=Text Color\: +txt_size=Text Size\: +wait_mid=Waiting (Mid Click) + +consent_title=Responsible Use Agreement +consent_msg=This tool is for learning and productivity. Using it in anti-cheat protected games risks account bans; you are responsible. Do you accept? +update_title=Update Available +update_msg=New version %s is available (you have %s). Open the download page? diff --git a/src/main/resources/i18n/messages_fr.properties b/src/main/resources/i18n/messages_fr.properties new file mode 100644 index 0000000..4312832 --- /dev/null +++ b/src/main/resources/i18n/messages_fr.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - fr +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Type d'action\: +add_act=(+) Ajouter +anti_ban=Mode Humain (Délais Aléat.) +apply_s=Appliquer +c_type=Type de clic\: +clr_act=(x) Effacer +col_pick=Choisir +cps=Vitesse Souris (CPS)\: +d_click=Double Clic +del_act=(-) Supprimer +fix_loc=Bouton Fixe +hook_fail=STATUT\: RACCOURCI INDISPONIBLE +info_cps=Définit la vitesse de clic (CPS). +info_hum=Anti-Ban\: Ajoute des fluctuations aléatoires aux délais. +info_lim=Arrête automatiquement l'automatisation. +info_px=Tolérance\: Empêche les petits changements de lumière de fausser la détection. +info_px_cond=Correspond\: Agit quand la couleur apparait. +info_px_rate=Taux de scan\: Fréquence de vérification (ms). +info_title=Info +io_export=Exporter (JSON) +io_fail=Echec du fichier\: +io_import=Importer (JSON) +key_cps=Vitesse Touche (/s)\: +key_trg=Touche Cible\: +l_click=Clic Gauche +lang_title=(L) Langue +lim_after=Dans\: +lim_invalid=STATUT\: LIMITE INVALIDE +lim_iter=Itérations +lim_min=Minutes +lim_shut=Éteindre le PC +lim_stop=Arrêter Macro +lim_title=Limites Arrêt Auto +lim_use=Activer Limiteur +list_add=[v] Ajouter +m_click=Clic Milieu +ms_delay=Délai (ms)\: +pick_loc=Choisir +press_k=Appuyez... +prof_del=Supprimer +prof_load=Charger +prof_save=Enregistrer +prof_title=(P) Profils +px_1=1. Pixel Cible +px_2=2. Conditions +px_3=3. Réaction +px_act3=Appuyer Touche +px_act4=Bip Sonore +px_clr=Couleur\: +px_cond1=Couleur CORRESPOND +px_cond2=Couleur CHANGE +px_rate=Vitesse Scan (Ms)\: +px_tol=Tolérance (%)\: +r_click=Clic Droit +rec_start=● Enregistrer +rec_status=STATUT\: ENREGISTREMENT (ESC) +rec_stop=■ Arreter (ESC) +reset=Réinit. +sched_countdown=STATUT\: DEBUT (%d s) +sched_sec=secondes après +sched_title=(Z) Planificateur +sched_use=Démarrage différé +set_hk=Raccourci Start/Stop\: +set_k=Définir +shut_cancel_btn=Annuler l'arret +shut_cancelled=Arret annule. +shut_fail=Echec de la commande d'arret\: +shut_ok=Limite atteinte\! Le PC s'eteindra dans %s. +shut_pending=Limite atteinte\! Arret dans %s.\nCliquez ci-dessous pour annuler. +shut_title=Arret du PC +shut_unsup=Arret automatique non supporte sur cet OS\: +st_idle=STATUT\: EN ATTENTE +st_lim=STATUT\: LIMITE +st_run=STATUT\: EN COURS +style=(O) Apparence UI +t_chain=Chaîne +t_key=Clavier +t_mouse=Souris +t_px=Pixel +t_set=Paramètres +tabhk_title=(K) Raccourcis par onglet +theme=Thème\: +theme_d=Sombre +theme_l=Clair +tnt_key=Appui Touche +tnt_mouse=Clic Souris +tnt_move=Bouger Souris +tray_exit=Quitter +tray_show=Afficher +tray_toggle=Demarrer / Arreter +txt_col=Couleur Texte\: +txt_size=Taille\: +wait_mid=Attente (Milieu) + +consent_title=Accord d'utilisation responsable +consent_msg=Cet outil sert a l'apprentissage et la productivite. Son usage dans des jeux anti-triche risque le bannissement; vous etes responsable. Acceptez-vous? +update_title=Mise a jour disponible +update_msg=Nouvelle version %s disponible (actuelle %s). Ouvrir la page de telechargement? diff --git a/src/main/resources/i18n/messages_it.properties b/src/main/resources/i18n/messages_it.properties new file mode 100644 index 0000000..b9409e3 --- /dev/null +++ b/src/main/resources/i18n/messages_it.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - it +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Tipo di Azione\: +add_act=(+) Aggiungi +anti_ban=Modalità Umana (Ritardo) +apply_s=Applica +c_type=Tipo di clic\: +clr_act=(x) Pulisci +col_pick=Seleziona +cps=Velocità Mouse (CPS)\: +d_click=Doppio Clic +del_act=(-) Rimuovi +fix_loc=Pos. Fissa +hook_fail=STATO\: HOTKEY NON DISPONIBILE +info_cps=Imposta i clic al secondo (CPS). +info_hum=Anti-Ban\: Aggiunge fluttuazioni casuali ai ritardi. +info_lim=Ferma l'automazione in base a limiti. +info_px=Tolleranza\: Evita che l'illuminazione rompa l'algoritmo. +info_px_cond=Corrisponde\: Agisce quando il colore appare. +info_px_rate=Velocità scan\: Frequenza di controllo schermo (ms). +info_title=Info +io_export=Esporta (JSON) +io_fail=Operazione file fallita\: +io_import=Importa (JSON) +key_cps=Velocità Tasto (/s)\: +key_trg=Tasto Obiettivo\: +l_click=Clic Sinistro +lang_title=(L) Lingua +lim_after=Dopo\: +lim_invalid=STATO\: LIMITE NON VALIDO +lim_iter=Iterazioni +lim_min=Minuti +lim_shut=Spegni PC +lim_stop=Ferma Macro +lim_title=Limiti Arresto Auto +lim_use=Abilita Limitatore +list_add=[v] Aggiungi +m_click=Clic Centrale +ms_delay=Ritardo (ms)\: +pick_loc=Seleziona +press_k=Premi... +prof_del=Elimina +prof_load=Carica +prof_save=Salva +prof_title=(P) Profili +px_1=1. Pixel Obiettivo +px_2=2. Condizioni +px_3=3. Reazione +px_act3=Premi Tasto +px_act4=Suona Solo +px_clr=Colore\: +px_cond1=Colore CORRISPONDE +px_cond2=Colore CAMBIA +px_rate=Velocità Scan (Ms)\: +px_tol=Tolleranza (%)\: +r_click=Clic Destro +rec_start=● Registra +rec_status=STATO\: REGISTRAZIONE (ESC) +rec_stop=■ Ferma (ESC) +reset=Reimposta +sched_countdown=STATO\: AVVIO (%d s) +sched_sec=secondi dopo +sched_title=(Z) Pianificatore +sched_use=Avvio ritardato +set_hk=Tasto Avvio/Arresto\: +set_k=Assegna +shut_cancel_btn=Annulla spegnimento +shut_cancelled=Spegnimento annullato. +shut_fail=Comando di spegnimento fallito\: +shut_ok=Limite raggiunto\! Il PC si spegnera tra %s. +shut_pending=Limite raggiunto\! Spegnimento tra %s.\nPremi sotto per annullare. +shut_title=Spegnimento PC +shut_unsup=Spegnimento automatico non supportato su questo OS\: +st_idle=STATO\: IN ATTESA +st_lim=STATO\: LIMITE RAGGIUNTO +st_run=STATO\: IN ESECUZIONE +style=(O) Aspetto UI +t_chain=Catena +t_key=Tastiera +t_mouse=Mouse +t_px=Pixel +t_set=Impostazioni +tabhk_title=(K) Tasti per scheda +theme=Tema Principale\: +theme_d=Modo Scuro +theme_l=Modo Chiaro +tnt_key=Pressione Tasto +tnt_mouse=Clic Mouse +tnt_move=Sposta Mouse +tray_exit=Esci +tray_show=Mostra +tray_toggle=Avvia / Ferma +txt_col=Colore Testo\: +txt_size=Dimensione\: +wait_mid=Attesa (Centrale) + +consent_title=Accordo di uso responsabile +consent_msg=Questo strumento e per apprendimento e produttivita. Uso in giochi con anti-cheat rischia il ban; sei responsabile. Accetti? +update_title=Aggiornamento disponibile +update_msg=Nuova versione %s disponibile (attuale %s). Aprire la pagina di download? diff --git a/src/main/resources/i18n/messages_ru.properties b/src/main/resources/i18n/messages_ru.properties new file mode 100644 index 0000000..11d2e6b --- /dev/null +++ b/src/main/resources/i18n/messages_ru.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - ru +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Тип Действия\: +add_act=(+) Добавить +anti_ban=Анти-Бан (случайная задержка) +apply_s=Применить +c_type=Тип клика\: +clr_act=(x) Очистить +col_pick=Выбрать Цвет +cps=Скорость Мыши (CPS)\: +d_click=Двойной Клик +del_act=(-) Удалить +fix_loc=Фикс. Место +hook_fail=СТАТУС\: ГОРЯЧАЯ КЛАВИША НЕДОСТУПНА +info_cps=Устанавливает кликов в секунду (CPS). +info_hum=Анти-Бан\: Добавляет случайные колебания к задержкам. +info_lim=Останавливает автоматизацию при достижении лимита. +info_px=Допуск\: Игнорирует изменения освещения. +info_px_cond=Совпадает\: Реагирует на появление цвета. +info_px_rate=Скорость\: Частота проверки экрана (мс). +info_title=Информация +io_export=Экспорт (JSON) +io_fail=Сбой работы с файлом\: +io_import=Импорт (JSON) +key_cps=Скор. Клавиатуры (/с)\: +key_trg=Целевая Клавиша\: +l_click=Левый Клик +lang_title=(L) Язык +lim_after=Лимит\: +lim_invalid=СТАТУС\: НЕВЕРНЫЙ ЛИМИТ +lim_iter=Итераций +lim_min=Минут +lim_shut=Выкл ПК +lim_stop=Только стоп +lim_title=Лимиты Автоостановки +lim_use=Вкл. Лимитер +list_add=[v] Добавить +m_click=Средний Клик +ms_delay=Задержка (мс)\: +pick_loc=Выбрать +press_k=Нажмите... +prof_del=Удалить +prof_load=Загрузить +prof_save=Сохранить +prof_title=(P) Профили +px_1=1. Цель Пиксель +px_2=2. Условия +px_3=3. Реакция +px_act3=Своя Клавиша +px_act4=Только Звук +px_clr=Цвет\: +px_cond1=Когда совпадает +px_cond2=Когда меняется +px_rate=Скорость (Мс)\: +px_tol=Допуск (%)\: +r_click=Правый Клик +rec_start=● Запись +rec_status=СТАТУС\: ЗАПИСЬ (ESC\=стоп) +rec_stop=■ Стоп (ESC) +reset=Сброс +sched_countdown=СТАТУС\: СТАРТ (%d с) +sched_sec=секунд спустя +sched_title=(Z) Планировщик +sched_use=Запуск с задержкой +set_hk=Горячая Клавиша\: +set_k=Назначить +shut_cancel_btn=Отменить выключение +shut_cancelled=Выключение отменено. +shut_fail=Сбой команды выключения\: +shut_ok=Лимит достигнут\! ПК выключится через %s. +shut_pending=Лимит достигнут\! Выключение через %s.\nНажмите ниже для отмены. +shut_title=Выключение ПК +shut_unsup=Автовыключение не поддерживается в этой ОС\: +st_idle=СТАТУС\: ОЖИДАНИЕ +st_lim=СТАТУС\: ЛИМИТ +st_run=СТАТУС\: РАБОТАЕТ +style=(O) Дизайн UI +t_chain=Цепь +t_key=Клавиатура +t_mouse=Мышь +t_px=Пиксель +t_set=Настройки +tabhk_title=(K) Горячие клавиши вкладок +theme=Тема\: +theme_d=Темный +theme_l=Светлый +tnt_key=Нажатие Клавиши +tnt_mouse=Клик Мыши +tnt_move=Сдвинуть Мышь +tray_exit=Выход +tray_show=Показать +tray_toggle=Старт / Стоп +txt_col=Цвет Текста\: +txt_size=Размер\: +wait_mid=Ожидание (Ср. клик) + +consent_title=Соглашение об ответственном использовании +consent_msg=Этот инструмент для обучения и продуктивности. Использование в играх с анти-читом грозит баном; ответственность на вас. Принимаете? +update_title=Доступно обновление +update_msg=Доступна новая версия %s (у вас %s). Открыть страницу загрузки? diff --git a/src/main/resources/i18n/messages_tr.properties b/src/main/resources/i18n/messages_tr.properties new file mode 100644 index 0000000..7b994ef --- /dev/null +++ b/src/main/resources/i18n/messages_tr.properties @@ -0,0 +1,100 @@ +#AutoClicker i18n - tr +#Sun Jun 21 15:07:13 GMT+03:00 2026 +act_typ=Eylem Turu\: +add_act=(+) Eylem Ekle +anti_ban=Insan Modu (Rastgele Gecikme) +apply_s=Uygula/Kaydet +c_type=Tiklama Tipi\: +clr_act=(x) Temizle +col_pick=Renk Sec +cps=Fare Hizi (CPS)\: +d_click=Cift Sol Tik +del_act=(-) Secileni Sil +fix_loc=Sabit Konuma Tikla +hook_fail=DURUM\: KISAYOL DINLENEMIYOR +info_cps=Saniyedeki tiklama hizini belirler (Click Per Second). +info_hum=Anti-Ban\: Robotik tiklamalari saptirmak icin gecikmelere minik sapmalar ekler. +info_lim=Otomasyonu belirli bir sure sonra kapatir. +info_px=Renk Toleransi\: Ufak golge farkliliklarinin renk algisini bozmasini engeller. +info_px_cond=Eslestiginde\: Renk gorundugunde tepki verir.\nDegistiginde\: Renk kayboldugunda tepki verir. +info_px_rate=Tarama Hizi\: Ekranin ne siklikla kontrol edilecegini belirler (Milisaniye). +info_title=Bilgi +io_export=Disa Aktar (JSON) +io_fail=Dosya islemi basarisiz\: +io_import=Ice Aktar (JSON) +key_cps=Klavye Hizi (Saniyede)\: +key_trg=Basilacak Tus\: +l_click=Sol Tik +lang_title=(L) Dil (Language) +lim_after=Sinir\: +lim_invalid=DURUM\: GECERSIZ LIMIT DEGERI +lim_iter=Dongu Sonra +lim_min=Dakika Sonra +lim_shut=Bilgisayari Kapat +lim_stop=Sadece Makroyu Durdur +lim_title=Otomatik Durdurma Sinirlari +lim_use=Limitoru Aktif Et +list_add=[v] Listeye Ekle +m_click=Orta Tik +ms_delay=Milisaniye (Gecikme)\: +pick_loc=Konum Sec +press_k=Basin... +prof_del=Sil +prof_load=Yukle +prof_save=Kaydet +prof_title=(P) Profiller +px_1=1. Gozetlenecek Piksel +px_2=2. Tetiklenme Sartlari +px_3=3. Gerceklesecek Tepki +px_act3=Ozel Tusa Bas +px_act4=Sadece Zili Cal +px_clr=Renk\: +px_cond1=Renk ESLESTIGINDE +px_cond2=Renk DEGISTIGINDE +px_rate=Tarama Hizi (Ms)\: +px_tol=Tolerans Payi (%)\: +r_click=Sag Tik +rec_start=● Kaydet +rec_status=DURUM\: KAYDEDILIYOR (ESC\=dur) +rec_stop=■ Durdur (ESC) +reset=Sifirla +sched_countdown=DURUM\: BASLIYOR (%d sn) +sched_sec=saniye sonra +sched_title=(Z) Zamanlayici +sched_use=Gecikmeli baslat +set_hk=Kisayol Tusu (Baslat/Durdur)\: +set_k=Tus Ata +shut_cancel_btn=Kapatmayi Iptal Et +shut_cancelled=Bilgisayar kapatma iptal edildi. +shut_fail=Kapatma komutu basarisiz oldu\: +shut_ok=Limit asildi\! Bilgisayar %s icinde kapatilacak. +shut_pending=Limit asildi\! Bilgisayar %s icinde kapanacak.\nVazgecmek icin asagidaki butona basin. +shut_title=Bilgisayar Kapatiliyor +shut_unsup=Bu isletim sisteminde otomatik kapatma desteklenmiyor\: +st_idle=DURUM\: BEKLIYOR +st_lim=DURUM\: LIMIT YENDI +st_run=DURUM\: CALISIYOR +style=(O) Gorunum Ozellestirme +t_chain=Zincir +t_key=Klavye +t_mouse=Fare +t_px=Piksel +t_set=Ayarlar +tabhk_title=(K) Sekme Kisayollari +theme=Ana Tema\: +theme_d=Karanlik Mod +theme_l=Aydinlik Mod +tnt_key=Klavye Tusu +tnt_mouse=Fare Tiklamasi +tnt_move=Fareyi Tasi +tray_exit=Cikis +tray_show=Goster +tray_toggle=Baslat / Durdur +txt_col=Metin Rengi\: +txt_size=Yazi Boyutu\: +wait_mid=Bekliyor (Orta Tik) + +consent_title=Sorumlu Kullanim Onayi +consent_msg=Bu arac ogrenme ve verimlilik icindir. Anti-cheat korumali oyunlarda kullanmak hesap yasaklanmasi riski tasir; sorumluluk size aittir. Kabul ediyor musunuz? +update_title=Guncelleme Mevcut +update_msg=Yeni surum %s yayinlandi (su an %s). Indirme sayfasini acalim mi? diff --git a/src/test/java/com/ohualtex/autoclicker/VersionCompareTest.java b/src/test/java/com/ohualtex/autoclicker/VersionCompareTest.java new file mode 100644 index 0000000..8e9b7f1 --- /dev/null +++ b/src/test/java/com/ohualtex/autoclicker/VersionCompareTest.java @@ -0,0 +1,38 @@ +package com.ohualtex.autoclicker; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** AutoClicker.isNewer surum karsilastirmasi (oto-guncelleme kontrolu icin). */ +class VersionCompareTest { + + @Test + void newerMajorAndMinor() { + assertTrue(AutoClicker.isNewer("9.0", "8.5")); + assertTrue(AutoClicker.isNewer("8.6", "8.5")); + } + + @Test + void numericNotLexical() { + // "8.10" lexical olarak "8.9"dan kucuk gorunur ama sayisal olarak buyuktur + assertTrue(AutoClicker.isNewer("8.10", "8.9")); + } + + @Test + void sameOrOlderIsNotNewer() { + assertFalse(AutoClicker.isNewer("8.5", "8.5")); + assertFalse(AutoClicker.isNewer("8.4", "8.5")); + } + + @Test + void differingSegmentCounts() { + assertTrue(AutoClicker.isNewer("8.5.1", "8.5")); + assertFalse(AutoClicker.isNewer("8.5", "8.5.1")); + } + + @Test + void malformedIsNotNewer() { + assertFalse(AutoClicker.isNewer("dev", "8.5")); + } +} diff --git a/src/test/java/com/ohualtex/autoclicker/i18n/LangTest.java b/src/test/java/com/ohualtex/autoclicker/i18n/LangTest.java new file mode 100644 index 0000000..26df8c2 --- /dev/null +++ b/src/test/java/com/ohualtex/autoclicker/i18n/LangTest.java @@ -0,0 +1,36 @@ +package com.ohualtex.autoclicker.i18n; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class LangTest { + + @AfterEach + void reset() { Lang.L = 0; } + + @Test + void returnsTurkishByDefault() { + Lang.L = 0; + assertEquals("Fare", Lang.get("t_mouse")); + } + + @Test + void switchesLanguagePerIndex() { + Lang.L = 1; assertEquals("Mouse", Lang.get("t_mouse")); // EN + Lang.L = 5; assertEquals("Мышь", Lang.get("t_mouse")); // RU (UTF-8 dogrulama) + } + + @Test + void unknownKeyReturnsKeyItself() { + Lang.L = 0; + assertEquals("nonexistent_key_xyz", Lang.get("nonexistent_key_xyz")); + } + + @Test + void outOfRangeIndexFallsBackToFirst() { + Lang.L = 99; + assertEquals("Fare", Lang.get("t_mouse")); + } +}