FFXIV Guide: Toggle language columns

Add language toggle buttons that work across multiple tables correctly + description toggle

As of 2025-04-17. See the latest version.

  1. // ==UserScript==
  2. // @name FFXIV Guide: Toggle language columns
  3. // @name:ko FFXIV Guide: 언어별 세로단 토글
  4. // @namespace http://tampermonkey.net/
  5. // @version 1.0
  6. // @description Add language toggle buttons that work across multiple tables correctly + description toggle
  7. // @description:ko 언어별로 세로단을 가리는 버튼을 추가하는 스크립트 입니다
  8. // @match https://ffxivguide.akurosia.org/translate/
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. const languageMap = {
  17. en: { label: '🇺🇸 EN', headers: ['Singular_en', 'Name_en', 'Description_en'] },
  18. de: { label: '🇩🇪 DE', headers: ['Singular_de', 'Name_de', 'Description_de'] },
  19. fr: { label: '🇫🇷 FR', headers: ['Singular_fr', 'Name_fr', 'Description_fr'] },
  20. ja: { label: '🇯🇵 JA', headers: ['Singular_ja', 'Name_ja', 'Description_ja'] },
  21. cn: { label: '🇨🇳 CN', headers: ['Singular_cn', 'Name_cn', 'Description_cn'] },
  22. ko: { label: '🇰🇷 KO', headers: ['Singular_ko', 'Name_ko', 'Description_ko'] },
  23. };
  24.  
  25. const hiddenLangs = new Set();
  26. const buttonRefs = {};
  27. let descriptionHidden = false;
  28.  
  29. function toggleLanguage(langKey) {
  30. const { headers } = languageMap[langKey];
  31. const shouldHide = !hiddenLangs.has(langKey);
  32.  
  33. const tables = document.querySelectorAll('table');
  34.  
  35. tables.forEach(table => {
  36. const ths = table.querySelectorAll('thead tr th');
  37. const indexes = [];
  38.  
  39. ths.forEach((th, i) => {
  40. const text = th.textContent.trim();
  41. if (headers.includes(text)) {
  42. indexes.push(i + 1);
  43. }
  44. });
  45.  
  46. indexes.forEach(index => {
  47. const cells = table.querySelectorAll(`th:nth-child(${index}), td:nth-child(${index})`);
  48. cells.forEach(cell => {
  49. cell.style.display = shouldHide ? 'none' : '';
  50. });
  51. });
  52. });
  53.  
  54. if (shouldHide) hiddenLangs.add(langKey);
  55. else hiddenLangs.delete(langKey);
  56.  
  57. updateButtonStyle(langKey);
  58. }
  59.  
  60. function updateButtonStyle(langKey) {
  61. const btn = buttonRefs[langKey];
  62. if (!btn) return;
  63. const isHidden = hiddenLangs.has(langKey);
  64. btn.style.opacity = isHidden ? '0.4' : '1';
  65. btn.style.backgroundColor = isHidden ? '#222' : '#444';
  66. }
  67.  
  68. function toggleDescriptionColumns() {
  69. descriptionHidden = !descriptionHidden;
  70.  
  71. const tables = document.querySelectorAll('table');
  72. tables.forEach(table => {
  73. const ths = table.querySelectorAll('thead tr th');
  74. const indexes = [];
  75.  
  76. ths.forEach((th, i) => {
  77. const text = th.textContent.trim();
  78. if (text.startsWith('Description_')) {
  79. indexes.push(i + 1);
  80. }
  81. });
  82.  
  83. indexes.forEach(index => {
  84. const cells = table.querySelectorAll(`th:nth-child(${index}), td:nth-child(${index})`);
  85. cells.forEach(cell => {
  86. cell.style.display = descriptionHidden ? 'none' : '';
  87. });
  88. });
  89. });
  90.  
  91. const btn = buttonRefs['description'];
  92. if (btn) {
  93. btn.style.opacity = descriptionHidden ? '0.4' : '1';
  94. btn.style.backgroundColor = descriptionHidden ? '#222' : '#444';
  95. }
  96. }
  97.  
  98. function insertToggleButtons() {
  99. const searchButton = document.getElementById('submit');
  100. if (!searchButton || !searchButton.parentElement) {
  101. setTimeout(insertToggleButtons, 300);
  102. return;
  103. }
  104.  
  105. const wrapper = document.createElement('div');
  106. wrapper.style.margin = '10px 0';
  107.  
  108. // Language buttons
  109. Object.entries(languageMap).forEach(([key, { label }]) => {
  110. const btn = document.createElement('button');
  111. btn.textContent = label;
  112. styleButton(btn);
  113. btn.addEventListener('click', () => toggleLanguage(key));
  114. wrapper.appendChild(btn);
  115. buttonRefs[key] = btn;
  116. });
  117.  
  118. // Description-only toggle
  119. const descBtn = document.createElement('button');
  120. descBtn.textContent = '📝 아이템 설명';
  121. styleButton(descBtn);
  122. descBtn.addEventListener('click', toggleDescriptionColumns);
  123. wrapper.appendChild(descBtn);
  124. buttonRefs['description'] = descBtn;
  125.  
  126. searchButton.parentElement.insertAdjacentElement('afterend', wrapper);
  127. console.log('[Tampermonkey] ✅ 언어 토글 버튼 + 아이템 설명 토글 완성!');
  128. }
  129.  
  130. function styleButton(btn) {
  131. btn.style.marginRight = '5px';
  132. btn.style.padding = '4px 8px';
  133. btn.style.fontSize = '12px';
  134. btn.style.cursor = 'pointer';
  135. btn.style.background = '#444';
  136. btn.style.color = '#fff';
  137. btn.style.border = '1px solid #888';
  138. btn.style.borderRadius = '4px';
  139. btn.style.transition = 'all 0.2s ease';
  140. }
  141.  
  142. insertToggleButtons();
  143. })();