0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘録】Google Sitesに用語集を簡単に載せる方法

Last updated at Posted at 2025-08-25

Google Sitesで、用語集を管理するようになったので、備忘録

こんなの。

スクリーンショット 2025-08-24 223030.png

スプレッドシートでメンテできるので、チームで管理できるので便利。Q&Aにも応用できる。

Google Sitesに用語集を簡単に載せる方法

スプレッドシート×Apps Scriptで作る検索機能付き動的用語集

Google Sitesに用語集を載せる一つの方法

企業の用語集について、以下のような課題をお持ちではないでしょうか:

PDFやWordの用語集 → 更新が大変、検索しにくい、Google Sitesに載せにくい
社内システムの奥に隠れた用語集 → アクセスしにくい、存在を忘れられがち
静的なHTMLページ → 作成・更新に技術スキルが必要
Google Sitesに用語集を載せる具体的な方法がわからない

今回は、Google Workspaceだけでこれらの課題を解決する一つの方法をご紹介します。


なぜこのシステムが企業に最適なのか

🎯 Google Sitesとの完璧な統合

  • 会社のWebサイトにiframe一行で埋め込み完了
  • Google Workspaceのシングルサインオンでセキュアなアクセス
  • 企業サイトのデザインと自然に統合

📊 スプレッドシートでメンテナンス

従来: HTML編集 → FTPアップロード → テスト → 公開
新方式: スプレッドシートに1行追加 → 完了!

💰 コスト0円

  • Googleの無料サービスのみ使用
  • サーバー不要、ドメイン不要、専用ソフト不要

👥 複数人管理が簡単

  • 各部署の担当者がスプレッドシートを直接編集
  • 権限管理はGoogleスプレッドシートの共有機能で完結
  • 編集履歴・コメント機能で運用も安心

完成イメージ:企業用語集の理想形

主な機能

  • リアルタイム検索: 用語・読み方・説明文から瞬時に検索
  • タグフィルタ: 「AI」「技術」「ツール」などで絞り込み
  • あいうえお順ナビ: 日本語用語のスムーズなブラウジング
  • アコーディオン表示: クリックで詳細表示/非表示
  • レスポンシブ対応: PC・スマホ・タブレット完全対応
  • 自動同期: スプレッドシート更新で即座にサイトに反映

🏢 企業サイトでの活用例

IT企業の場合

https://company.com/glossary
┌─────────────────────────────────┐
│ ◯◯株式会社 - 技術用語集         │
├─────────────────────────────────┤
│ 🔍 [検索ボックス]              │
│ タグ: [AI] [クラウド] [セキュリティ] │
│                                 │
│ 【API】                         │
│ 読み方: エーピーアイ           │
│ ▼ 説明: アプリケーション間の... │
│                                 │
│ 【Gemini】                      │
│ 読み方: ジェミニ               │
│ ▼ 説明: Googleが開発した...     │
└─────────────────────────────────┘

医療機関の場合

◯◯病院 - 医療用語集
- 診療科別フィルタ
- 緊急度別色分け
- 患者様向け平易な説明

製造業の場合

◯◯工業 - 技術用語集  
- 工程別分類
- 安全基準用語
- 品質管理用語

2つのアプローチから選択可能

🔄 アプローチの選択

用語集の構成に応じて、以下の3つのアプローチからお選びいただけます:

パターンA: 統合版(1つのアプリ)

  • 日本語・英語・数字すべてを1つのアプリで管理
  • あいうえお順 + A-Z + 数字のナビゲーション
  • シンプルな構成でメンテナンスしやすい

パターンB: 日本語専用版

  • ひらがな・カタカナ・漢字の用語のみ
  • あ行〜わ行の美しいナビゲーション
  • 日本語に特化した最適化

パターンC: 英語専用版

  • アルファベット・数字で始まる用語のみ
  • A〜Z + 0-9のクリアなナビゲーション
  • 英語圏の用語集スタイル

💡 どのアプローチを選ぶべきか

用語の構成 おすすめアプローチ 理由
日英混在(少量) 統合版 シンプルで管理しやすい
日本語メイン 日本語専用版 美しいあいうえお順ナビ
英語メイン 英語専用版 アルファベット順の明快さ
大量の日英混在 2つの専用版 ユーザーが迷わない

アプローチA: 統合版の実装

Step 1: スプレッドシートの準備

データ構造(4列構成)

A列:用語 B列:読み方 C列:説明 D列:タグ
Gemini ジェミニ Googleの大規模言語モデル AI,Google
API エーピーアイ アプリケーション間の連携仕様 技術,Web
テキスト埋め込み テキストうめこみ 文章を数値ベクトルに変換する技術 技術

サンプルデータ例

用語,読み方,説明,タグ
Gemini,ジェミニ,Googleが開発した、マルチモーダルに対応した大規模言語モデルです,AI,Google
AI,エーアイ,人間のように学習や推論、判断を行うコンピューター技術やシステムです,AI,技術
テキスト埋め込み,テキストうめこみ,文章を数値のベクトルに変換する技術,技術
HTML,エイチティーエムエル,ウェブページの構造を記述するためのマークアップ言語です,技術,Web
スプレッドシート,スプレッドシート,表計算ソフトです。データ管理、分析、視覚化に役立ちます,ツール,Google

Step 2: Google Apps Scriptの設定

2.1 スクリプトエディタを開く

  1. Googleスプレッドシートで「拡張機能」→「Apps Script」
  2. プロジェクト名を「企業用語集」に変更

2.2 Code.gs の実装

function doGet(e) {
  // あなたのスプレッドシートIDに変更してください
  const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID_HERE';
  const SHEET_NAME = 'シート1';
  
  try {
    const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
    const sheet = ss.getSheetByName(SHEET_NAME);
    
    if (!sheet) {
      return HtmlService.createHtmlOutput('シートが見つかりませんでした。');
    }
    
    const data = sheet.getDataRange().getValues();
    const termsData = data.slice(1); // ヘッダー行をスキップ
    
    // 用語を五十音順でソート
    termsData.sort((a, b) => {
      const termA = a[0] || '';
      const termB = b[0] || '';
      return termA.localeCompare(termB, 'ja', { numeric: true });
    });
    
    const tagsSet = new Set();
    const termsByCategory = {};
    
    termsData.forEach(row => {
      const term = row[0] || '';
      const reading = row[1] || '';
      const description = row[2] || '';
      const tagsString = row[3] || '';
      
      const tags = tagsString.split(',').map(tag => tag.trim()).filter(tag => tag);
      tags.forEach(tag => tagsSet.add(tag));
      
      const category = getCategory(term);
      
      if (!termsByCategory[category]) {
        termsByCategory[category] = [];
      }
      termsByCategory[category].push({ term, reading, description, tags });
    });
    
    const template = HtmlService.createTemplateFromFile('index');
    template.termsData = termsByCategory;
    template.tags = Array.from(tagsSet).sort();
    
    return template.evaluate()
      .setTitle('企業用語集')
      .setSandboxMode(HtmlService.SandboxMode.IFRAME);
      
  } catch (err) {
    return HtmlService.createHtmlOutput(`エラーが発生しました: ${err.message}`);
  }
}

// 用語を適切なカテゴリに分類
function getCategory(term) {
  if (!term) return 'その他';
  
  const firstChar = term.charAt(0);
  
  // ひらがな分類
  if (firstChar >= '' && firstChar <= '') {
    if (firstChar >= '' && firstChar <= '') return 'あ行';
    if (firstChar >= '' && firstChar <= '') return 'か行';
    if (firstChar >= '' && firstChar <= '') return 'さ行';
    if (firstChar >= '' && firstChar <= '') return 'た行';
    if (firstChar >= '' && firstChar <= '') return 'な行';
    if (firstChar >= '' && firstChar <= '') return 'は行';
    if (firstChar >= '' && firstChar <= '') return 'ま行';
    if (firstChar >= '' && firstChar <= '') return 'や行';
    if (firstChar >= '' && firstChar <= '') return 'ら行';
    if (firstChar >= '' && firstChar <= '') return 'わ行';
  }
  
  // カタカナ分類
  if (firstChar >= '' && firstChar <= '') {
    if (firstChar >= '' && firstChar <= '') return 'あ行';
    if (firstChar >= '' && firstChar <= '') return 'か行';
    if (firstChar >= '' && firstChar <= '') return 'さ行';
    if (firstChar >= '' && firstChar <= '') return 'た行';
    if (firstChar >= '' && firstChar <= '') return 'な行';
    if (firstChar >= '' && firstChar <= '') return 'は行';
    if (firstChar >= '' && firstChar <= '') return 'ま行';
    if (firstChar >= '' && firstChar <= '') return 'や行';
    if (firstChar >= '' && firstChar <= '') return 'ら行';
    if (firstChar >= '' && firstChar <= '') return 'わ行';
  }
  
  // アルファベット
  if ((firstChar >= 'A' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'z')) {
    return 'A-Z';
  }
  
  // 数字
  if (firstChar >= '0' && firstChar <= '9') {
    return '数字';
  }
  
  return 'その他';
}

2.3 index.html の作成

「ファイル」→「新規」→「HTMLファイル」でindex.htmlを作成

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    body { 
      font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Meiryo', sans-serif; 
    }
    .accordion-content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease-out;
    }
    .accordion-item.open .accordion-content {
      max-height: 500px;
    }
    
    .tag-button {
      background-color: #e5e7eb;
      color: #374151;
      font-weight: 600;
      font-size: 0.875rem;
      padding: 0.25rem 0.75rem;
      border-radius: 9999px;
      transition-property: background-color, color;
      transition-duration: 0.2s;
    }
    
    .tag-button:hover {
      background-color: #d1d5db;
    }
    
    .tag-button.active {
      background-color: #3b82f6;
      color: white;
    }
    
    .fixed-nav {
      position: sticky;
      top: 0;
      background-color: white;
      z-index: 10;
      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    }
    
    .glossary-container {
      padding-bottom: 50vh;
    }
    
    .nav-buttons {
      display: flex;
      flex-wrap: wrap;
      gap: 0.5rem;
      justify-content: center;
    }
    
    .nav-buttons a {
      padding: 0.5rem 1rem;
      background-color: #f3f4f6;
      border-radius: 0.5rem;
      font-weight: 600;
      color: #374151;
      text-decoration: none;
      transition: all 0.2s;
    }
    
    .nav-buttons a:hover {
      background-color: #3b82f6;
      color: white;
      transform: translateY(-1px);
    }
  </style>
</head>
<body class="bg-gray-100 p-4 sm:p-8">
  <div class="max-w-4xl mx-auto bg-white rounded-xl shadow-lg p-6 sm:p-10">
    <h1 class="text-3xl sm:text-4xl font-bold text-center text-gray-800 mb-6">企業用語集</h1>
    
    <div class="fixed-nav flex flex-col gap-4 py-4 px-6 mx-6 mb-6">
      <!-- 検索ボックス -->
      <div class="relative mb-4">
        <input type="text" id="searchInput" placeholder="用語を検索..." 
               class="w-full px-4 py-2 border rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 pl-10">
        <svg class="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
        </svg>
      </div>
      
      <!-- タグフィルター -->
      <div class="flex flex-wrap gap-2">
        <span class="text-gray-600 mr-2">タグ:</span>
        <div class="flex flex-wrap gap-2">
          <button onclick="filterByTag('all', this)" class="tag-button active">すべて</button>
          <? for (let i = 0; i < tags.length; i++) { ?>
          <button onclick="filterByTag('<?= tags[i] ?>', this)" class="tag-button"><?= tags[i] ?></button>
          <? } ?>
        </div>
      </div>
      
      <!-- ナビゲーション -->
      <div class="nav-buttons">
        <a href="#" onclick="scrollToCategory('あ行')">あ行</a>
        <a href="#" onclick="scrollToCategory('か行')">か行</a>
        <a href="#" onclick="scrollToCategory('さ行')">さ行</a>
        <a href="#" onclick="scrollToCategory('た行')">た行</a>
        <a href="#" onclick="scrollToCategory('な行')">な行</a>
        <a href="#" onclick="scrollToCategory('は行')">は行</a>
        <a href="#" onclick="scrollToCategory('ま行')">ま行</a>
        <a href="#" onclick="scrollToCategory('や行')">や行</a>
        <a href="#" onclick="scrollToCategory('ら行')">ら行</a>
        <a href="#" onclick="scrollToCategory('わ行')">わ行</a>
        <a href="#" onclick="scrollToCategory('A-Z')">A-Z</a>
        <a href="#" onclick="scrollToCategory('数字')">数字</a>
        <a href="#" onclick="scrollToCategory('その他')">その他</a>
      </div>
    </div>
    
    <!-- 用語リスト -->
    <div id="glossary-container" class="glossary-container">
      <? 
      const categoryOrder = ['あ行', 'か行', 'さ行', 'た行', 'な行', 'は行', 'ま行', 'や行', 'ら行', 'わ行', 'A-Z', '数字', 'その他'];
      categoryOrder.forEach(category => { 
        if (termsData[category]) {
          const terms = termsData[category];
      ?>
      <h2 id="<?= category ?>" class="text-2xl sm:text-3xl font-bold text-blue-600 mt-8 mb-4 pt-16 -mt-16"><?= category ?></h2>
      <? terms.forEach(term => { ?>
      <div class="accordion-item p-4 border rounded-lg mb-2 cursor-pointer hover:bg-gray-50 transition-colors" onclick="toggleAccordion(this)">
        <div class="flex justify-between items-start mb-2">
          <div class="flex flex-col">
            <div class="flex flex-wrap items-baseline gap-2">
              <span class="text-xl sm:text-2xl font-semibold text-gray-800"><?= term.term ?></span>
              <? if (term.reading) { ?>
              <span class="text-sm text-gray-500"><?= term.reading ?></span>
              <? } ?>
            </div>
          </div>
          <? if (term.tags && term.tags.length > 0) { ?>
          <div class="flex flex-wrap gap-1 ml-2">
            <? term.tags.forEach(tag => { ?>
            <span class="bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded-full"><?= tag ?></span>
            <? }); ?>
          </div>
          <? } ?>
        </div>
        <div class="accordion-content mt-2">
          <p class="text-gray-700 leading-relaxed"><?= term.description ?></p>
        </div>
      </div>
      <? }); ?>
      <? } 
      }); ?>
    </div>
  </div>

  <script>
    let allTermItems = [];
    
    document.addEventListener('DOMContentLoaded', () => {
      allTermItems = Array.from(document.querySelectorAll('.accordion-item'));
    });
    
    function toggleAccordion(element) {
      element.classList.toggle('open');
      const content = element.querySelector('.accordion-content');
      if (element.classList.contains('open')) {
        content.style.maxHeight = content.scrollHeight + 'px';
      } else {
        content.style.maxHeight = '0';
      }
    }
    
    function filterByTag(tag, clickedButton) {
      const allButtons = document.querySelectorAll('.tag-button');
      allButtons.forEach(btn => btn.classList.remove('active'));
      clickedButton.classList.add('active');
      
      const searchTerm = document.getElementById('searchInput').value.toLowerCase();
      
      allTermItems.forEach(item => item.style.display = 'none');
      
      const filteredItems = allTermItems.filter(item => {
        const termText = item.querySelector('.text-xl').textContent.toLowerCase();
        const readingElement = item.querySelector('.text-sm');
        const readingText = readingElement ? readingElement.textContent.toLowerCase() : '';
        const descriptionText = item.querySelector('p').textContent.toLowerCase();
        
        const tagsContainer = item.querySelector('.flex-wrap.gap-1.ml-2');
        let tagMatch = false;
        if (tag === 'all') {
          tagMatch = true;
        } else if (tagsContainer) {
          const tagElements = tagsContainer.querySelectorAll('span');
          const tagTexts = Array.from(tagElements).map(span => span.textContent);
          tagMatch = tagTexts.includes(tag);
        }
        
        let searchMatch = false;
        if (searchTerm === '') {
          searchMatch = true;
        } else {
          searchMatch = termText.includes(searchTerm) || readingText.includes(searchTerm) || descriptionText.includes(searchTerm);
        }
        
        return tagMatch && searchMatch;
      });
      
      filteredItems.forEach(item => item.style.display = 'block');
      updateHeadingsVisibility();
    }
    
    function filterBySearch() {
      const allButtons = document.querySelectorAll('.tag-button');
      allButtons.forEach(btn => btn.classList.remove('active'));
      document.querySelector('.tag-button').classList.add('active');
      
      const searchTerm = document.getElementById('searchInput').value.toLowerCase();
      
      allTermItems.forEach(item => item.style.display = 'none');
      
      const filteredItems = allTermItems.filter(item => {
        const termText = item.querySelector('.text-xl').textContent.toLowerCase();
        const readingElement = item.querySelector('.text-sm');
        const readingText = readingElement ? readingElement.textContent.toLowerCase() : '';
        const descriptionText = item.querySelector('p').textContent.toLowerCase();
        
        return termText.includes(searchTerm) || readingText.includes(searchTerm) || descriptionText.includes(searchTerm);
      });
      
      filteredItems.forEach(item => item.style.display = 'block');
      updateHeadingsVisibility();
    }
    
    function updateHeadingsVisibility() {
      const headings = document.querySelectorAll('#glossary-container h2');
      headings.forEach(heading => {
        let hasVisibleTerm = false;
        let nextSibling = heading.nextElementSibling;
        while (nextSibling && nextSibling.tagName.toLowerCase() !== 'h2') {
          if (nextSibling.style.display !== 'none') {
            hasVisibleTerm = true;
            break;
          }
          nextSibling = nextSibling.nextElementSibling;
        }
        heading.style.display = hasVisibleTerm ? 'block' : 'none';
      });
    }
    
    function scrollToCategory(category) {
      document.getElementById('searchInput').value = '';
      filterBySearch();
      
      const element = document.getElementById(category);
      if (element) {
        const navHeight = document.querySelector('.fixed-nav').offsetHeight;
        const offsetPosition = element.offsetTop - navHeight;
        window.scrollTo({
          top: offsetPosition,
          behavior: 'smooth'
        });
      }
    }
    
    document.getElementById('searchInput').addEventListener('input', filterBySearch);
  </script>
</body>
</html>

Step 3: Webアプリとしてデプロイ

  1. 「デプロイ」→「新しいデプロイ」 をクリック
  2. 種類: 「ウェブアプリ」を選択
  3. 実行者: 「自分」
  4. アクセス権限: 「組織内の全員」を選択
  5. 「デプロイ」 をクリック
  6. WebアプリURLをコピー

Step 4: Google Sitesに埋め込み

4.1 Google Sitesでページ作成

  1. Google Sitesで企業サイトを開く
  2. 「用語集」ページを作成

4.2 iframe埋め込み

  1. 「挿入」→「埋め込み」→「URL」
  2. Apps ScriptのWebアプリURLを貼り付け
  3. 幅: 100%、高さ: 800px に設定
<iframe src="https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec" 
        width="100%" 
        height="800" 
        frameborder="0">
</iframe>

アプローチB: 日本語専用版の実装

特徴

  • 対象: ひらがな・カタカナ・漢字で始まる用語のみ
  • ナビゲーション: あ行〜わ行 + その他
  • デザイン: エメラルドグリーン系(日本的な色合い)
  • 最適化: 日本語フォント、行間、文字サイズ

Google Apps Script(日本語専用版)

Code.gs

// 日本語用語集専用版
function doGet(e) {
  const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID_HERE';
  const SHEET_NAME = 'シート1';
  
  try {
    const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
    const sheet = ss.getSheetByName(SHEET_NAME);
    
    if (!sheet) {
      return HtmlService.createHtmlOutput('シートが見つかりませんでした。');
    }
    
    const data = sheet.getDataRange().getValues();
    const termsData = data.slice(1);
    
    // 日本語用語のみを抽出
    const japaneseTerms = termsData.filter(row => {
      const term = row[0] || '';
      return isJapaneseTerm(term);
    });
    
    // 日本語の自然なソート
    japaneseTerms.sort((a, b) => {
      const termA = a[0] || '';
      const termB = b[0] || '';
      return termA.localeCompare(termB, 'ja', { numeric: true });
    });
    
    const tagsSet = new Set();
    const termsByCategory = {};
    
    japaneseTerms.forEach(row => {
      const term = row[0] || '';
      const reading = row[1] || '';
      const description = row[2] || '';
      const tagsString = row[3] || '';
      
      const tags = tagsString.split(',').map(tag => tag.trim()).filter(tag => tag);
      tags.forEach(tag => tagsSet.add(tag));
      
      const category = getJapaneseCategory(term);
      
      if (!termsByCategory[category]) {
        termsByCategory[category] = [];
      }
      termsByCategory[category].push({ term, reading, description, tags });
    });
    
    const template = HtmlService.createTemplateFromFile('index');
    template.termsData = termsByCategory;
    template.tags = Array.from(tagsSet).sort();
    
    return template.evaluate()
      .setTitle('日本語用語集')
      .setSandboxMode(HtmlService.SandboxMode.IFRAME);
      
  } catch (err) {
    return HtmlService.createHtmlOutput(`エラーが発生しました: ${err.message}`);
  }
}

// 日本語用語かどうかを判定
function isJapaneseTerm(term) {
  if (!term || term.length === 0) return false;
  
  const firstChar = term.charAt(0);
  
  // アルファベットで始まる場合は除外
  if ((firstChar >= 'A' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'z')) {
    return false;
  }
  
  // 数字で始まる場合も除外
  if (firstChar >= '0' && firstChar <= '9') {
    return false;
  }
  
  return true;
}

// 日本語用語を行別に分類
function getJapaneseCategory(term) {
  if (!term || term.length === 0) return 'その他';
  
  const firstChar = term.charAt(0);
  
  // ひらがなの分類
  if (firstChar >= '' && firstChar <= '') {
    if (firstChar >= '' && firstChar <= '') return 'あ行';
    if (firstChar >= '' && firstChar <= '') return 'か行';
    if (firstChar >= '' && firstChar <= '') return 'さ行';
    if (firstChar >= '' && firstChar <= '') return 'た行';
    if (firstChar >= '' && firstChar <= '') return 'な行';
    if (firstChar >= '' && firstChar <= '') return 'は行';
    if (firstChar >= '' && firstChar <= '') return 'ま行';
    if (firstChar >= '' && firstChar <= '') return 'や行';
    if (firstChar >= '' && firstChar <= '') return 'ら行';
    if (firstChar >= '' && firstChar <= '') return 'わ行';
  }
  
  // カタカナの分類
  if (firstChar >= '' && firstChar <= '') {
    if (firstChar >= '' && firstChar <= '') return 'あ行';
    if (firstChar >= '' && firstChar <= '') return 'か行';
    if (firstChar >= '' && firstChar <= '') return 'さ行';
    if (firstChar >= '' && firstChar <= '') return 'た行';
    if (firstChar >= '' && firstChar <= '') return 'な行';
    if (firstChar >= '' && firstChar <= '') return 'は行';
    if (firstChar >= '' && firstChar <= '') return 'ま行';
    if (firstChar >= '' && firstChar <= '') return 'や行';
    if (firstChar >= '' && firstChar <= '') return 'ら行';
    if (firstChar >= '' && firstChar <= '') return 'わ行';
  }
  
  return 'その他';
}

index.html(日本語専用版)

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    body { 
      font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Noto Sans CJK JP', 'Meiryo', sans-serif; 
    }
    .accordion-content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease-out;
    }
    .accordion-item.open .accordion-content {
      max-height: 500px;
    }
    
    .tag-button {
      background-color: #e5e7eb;
      color: #374151;
      font-weight: 600;
      font-size: 0.875rem;
      padding: 0.25rem 0.75rem;
      border-radius: 9999px;
      transition-property: background-color, color;
      transition-duration: 0.2s;
    }
    
    .tag-button:hover {
      background-color: #d1d5db;
    }
    
    .tag-button.active {
      background-color: #10b981;
      color: white;
    }
    
    .fixed-nav {
      position: sticky;
      top: 0;
      background-color: white;
      z-index: 10;
      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    }
    
    .glossary-container {
      padding-bottom: 50vh;
    }
    
    .japanese-nav {
      display: flex;
      flex-wrap: wrap;
      gap: 0.75rem;
      justify-content: center;
    }
    
    .japanese-nav a {
      padding: 0.75rem 1.5rem;
      background-color: #dcfce7;
      border-radius: 1rem;
      font-weight: 700;
      color: #166534;
      text-decoration: none;
      transition: all 0.2s;
      font-size: 1.1rem;
    }
    
    .japanese-nav a:hover {
      background-color: #10b981;
      color: white;
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
    }
  </style>
</head>
<body class="bg-gradient-to-br from-green-50 to-emerald-100 p-4 sm:p-8">
  <div class="max-w-4xl mx-auto bg-white rounded-xl shadow-lg p-6 sm:p-10">
    <h1 class="text-3xl sm:text-4xl font-bold text-center text-emerald-800 mb-2">日本語用語集</h1>
    <p class="text-center text-gray-600 mb-8">ひらがな・カタカナ・漢字の用語を検索</p>
    
    <div class="fixed-nav flex flex-col gap-4 py-4 px-6 mx-6 mb-6">
      <!-- 検索ボックス -->
      <div class="relative mb-4">
        <input type="text" id="searchInput" placeholder="日本語用語を検索..." 
               class="w-full px-4 py-2 border-2 border-emerald-200 rounded-full focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 pl-10">
        <svg class="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
        </svg>
      </div>
      
      <!-- タグフィルター -->
      <div class="flex flex-wrap gap-2">
        <span class="text-gray-600 mr-2">タグ:</span>
        <div class="flex flex-wrap gap-2">
          <button onclick="filterByTag('all', this)" class="tag-button active">すべて</button>
          <? for (let i = 0; i < tags.length; i++) { ?>
          <button onclick="filterByTag('<?= tags[i] ?>', this)" class="tag-button"><?= tags[i] ?></button>
          <? } ?>
        </div>
      </div>
      
      <!-- 日本語専用ナビゲーション -->
      <div class="japanese-nav">
        <a href="#" onclick="scrollToCategory('あ行')">あ行</a>
        <a href="#" onclick="scrollToCategory('か行')">か行</a>
        <a href="#" onclick="scrollToCategory('さ行')">さ行</a>
        <a href="#" onclick="scrollToCategory('た行')">た行</a>
        <a href="#" onclick="scrollToCategory('な行')">な行</a>
        <a href="#" onclick="scrollToCategory('は行')">は行</a>
        <a href="#" onclick="scrollToCategory('ま行')">ま行</a>
        <a href="#" onclick="scrollToCategory('や行')">や行</a>
        <a href="#" onclick="scrollToCategory('ら行')">ら行</a>
        <a href="#" onclick="scrollToCategory('わ行')">わ行</a>
        <a href="#" onclick="scrollToCategory('その他')">その他</a>
      </div>
    </div>
    
    <!-- 用語リスト -->
    <div id="glossary-container" class="glossary-container">
      <? 
      const categoryOrder = ['あ行', 'か行', 'さ行', 'た行', 'な行', 'は行', 'ま行', 'や行', 'ら行', 'わ行', 'その他'];
      categoryOrder.forEach(category => { 
        if (termsData[category]) {
          const terms = termsData[category];
      ?>
      <h2 id="<?= category ?>" class="text-2xl sm:text-3xl font-bold text-emerald-600 mt-8 mb-4 pt-16 -mt-16"><?= category ?></h2>
      <? terms.forEach(term => { ?>
      <div class="accordion-item p-4 border border-emerald-200 rounded-lg mb-2 cursor-pointer hover:bg-emerald-50 transition-colors" onclick="toggleAccordion(this)">
        <div class="flex justify-between items-start mb-2">
          <div class="flex flex-col">
            <div class="flex flex-wrap items-baseline gap-2">
              <span class="text-xl sm:text-2xl font-semibold text-gray-800"><?= term.term ?></span>
              <? if (term.reading) { ?>
              <span class="text-sm text-gray-500"><?= term.reading ?></span>
              <? } ?>
            </div>
          </div>
          <? if (term.tags && term.tags.length > 0) { ?>
          <div class="flex flex-wrap gap-1 ml-2">
            <? term.tags.forEach(tag => { ?>
            <span class="bg-emerald-100 text-emerald-800 text-xs font-medium px-2.5 py-0.5 rounded-full"><?= tag ?></span>
            <? }); ?>
          </div>
          <? } ?>
        </div>
        <div class="accordion-content mt-2">
          <p class="text-gray-700 leading-relaxed"><?= term.description ?></p>
        </div>
      </div>
      <? }); ?>
      <? } 
      }); ?>
    </div>
  </div>

  <script>
    let allTermItems = [];
    
    document.addEventListener('DOMContentLoaded', () => {
      allTermItems = Array.from(document.querySelectorAll('.accordion-item'));
    });
    
    function toggleAccordion(element) {
      element.classList.toggle('open');
      const content = element.querySelector('.accordion-content');
      if (element.classList.contains('open')) {
        content.style.maxHeight = content.scrollHeight + 'px';
      } else {
        content.style.maxHeight = '0';
      }
    }
    
    function filterByTag(tag, clickedButton) {
      const allButtons = document.querySelectorAll('.tag-button');
      allButtons.forEach(btn => btn.classList.remove('active'));
      clickedButton.classList.add('active');
      
      const searchTerm = document.getElementById('searchInput').value.toLowerCase();
      
      allTermItems.forEach(item => item.style.display = 'none');
      
      const filteredItems = allTermItems.filter(item => {
        const termText = item.querySelector('.text-xl').textContent.toLowerCase();
        const readingElement = item.querySelector('.text-sm');
        const readingText = readingElement ? readingElement.textContent.toLowerCase() : '';
        const descriptionText = item.querySelector('p').textContent.toLowerCase();
        
        const tagsContainer = item.querySelector('.flex-wrap.gap-1.ml-2');
        let tagMatch = false;
        if (tag === 'all') {
          tagMatch = true;
        } else if (tagsContainer) {
          const tagElements = tagsContainer.querySelectorAll('span');
          const tagTexts = Array.from(tagElements).map(span => span.textContent);
          tagMatch = tagTexts.includes(tag);
        }
        
        let searchMatch = false;
        if (searchTerm === '') {
          searchMatch = true;
        } else {
          searchMatch = termText.includes(searchTerm) || readingText.includes(searchTerm) || descriptionText.includes(searchTerm);
        }
        
        return tagMatch && searchMatch;
      });
      
      filteredItems.forEach(item => item.style.display = 'block');
      updateHeadingsVisibility();
    }
    
    function filterBySearch() {
      const allButtons = document.querySelectorAll('.tag-button');
      allButtons.forEach(btn => btn.classList.remove('active'));
      document.querySelector('.tag-button').classList.add('active');
      
      const searchTerm = document.getElementById('searchInput').value.toLowerCase();
      
      allTermItems.forEach(item => item.style.display = 'none');
      
      const filteredItems = allTermItems.filter(item => {
        const termText = item.querySelector('.text-xl').textContent.toLowerCase();
        const readingElement = item.querySelector('.text-sm');
        const readingText = readingElement ? readingElement.textContent.toLowerCase() : '';
        const descriptionText = item.querySelector('p').textContent.toLowerCase();
        
        return termText.includes(searchTerm) || readingText.includes(searchTerm) || descriptionText.includes(searchTerm);
      });
      
      filteredItems.forEach(item => item.style.display = 'block');
      updateHeadingsVisibility();
    }
    
    function updateHeadingsVisibility() {
      const headings = document.querySelectorAll('#glossary-container h2');
      headings.forEach(heading => {
        let hasVisibleTerm = false;
        let nextSibling = heading.nextElementSibling;
        while (nextSibling && nextSibling.tagName.toLowerCase() !== 'h2') {
          if (nextSibling.style.display !== 'none') {
            hasVisibleTerm = true;
            break;
          }
          nextSibling = nextSibling.nextElementSibling;
        }
        heading.style.display = hasVisibleTerm ? 'block' : 'none';
      });
    }
    
    function scrollToCategory(category) {
      document.getElementById('searchInput').value = '';
      filterBySearch();
      
      const element = document.getElementById(category);
      if (element) {
        const navHeight = document.querySelector('.fixed-nav').offsetHeight;
        const offsetPosition = element.offsetTop - navHeight;
        window.scrollTo({
          top: offsetPosition,
          behavior: 'smooth'
        });
      }
    }
    
    document.getElementById('searchInput').addEventListener('input', filterBySearch);
  </script>
</body>
</html>

アプローチC: 英語専用版の実装

特徴

  • 対象: アルファベット・数字で始まる用語のみ
  • ナビゲーション: A〜Z + 0-9 + Other
  • デザイン: ブルー系(国際的でプロフェッショナル)
  • 最適化: 英語フォント、アルファベット順ソート

Google Apps Script(英語専用版)

Code.gs

// 英語用語集専用版
function doGet(e) {
  const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID_HERE';
  const SHEET_NAME = 'シート1';
  
  try {
    const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
    const sheet = ss.getSheetByName(SHEET_NAME);
    
    if (!sheet) {
      return HtmlService.createHtmlOutput('シートが見つかりませんでした。');
    }
    
    const data = sheet.getDataRange().getValues();
    const termsData = data.slice(1);
    
    // 英語用語のみを抽出
    const englishTerms = termsData.filter(row => {
      const term = row[0] || '';
      return isEnglishTerm(term);
    });
    
    // アルファベット順でソート
    englishTerms.sort((a, b) => {
      const termA = a[0] || '';
      const termB = b[0] || '';
      return termA.localeCompare(termB, 'en', { numeric: true });
    });
    
    const tagsSet = new Set();
    const termsByCategory = {};
    
    englishTerms.forEach(row => {
      const term = row[0] || '';
      const reading = row[1] || '';
      const description = row[2] || '';
      const tagsString = row[3] || '';
      
      const tags = tagsString.split(',').map(tag => tag.trim()).filter(tag => tag);
      tags.forEach(tag => tagsSet.add(tag));
      
      const category = getEnglishCategory(term);
      
      if (!termsByCategory[category]) {
        termsByCategory[category] = [];
      }
      termsByCategory[category].push({ term, reading, description, tags });
    });
    
    const template = HtmlService.createTemplateFromFile('index');
    template.termsData = termsByCategory;
    template.tags = Array.from(tagsSet).sort();
    
    return template.evaluate()
      .setTitle('英語用語集')
      .setSandboxMode(HtmlService.SandboxMode.IFRAME);
      
  } catch (err) {
    return HtmlService.createHtmlOutput(`エラーが発生しました: ${err.message}`);
  }
}

// 英語用語かどうかを判定
function isEnglishTerm(term) {
  if (!term || term.length === 0) return false;
  
  const firstChar = term.charAt(0);
  
  // アルファベットで始まる場合は英語用語
  if ((firstChar >= 'A' && firstChar <= 'Z') || (firstChar >= 'a' && firstChar <= 'z')) {
    return true;
  }
  
  // 数字で始まる場合も英語セクションに含める
  if (firstChar >= '0' && firstChar <= '9') {
    return true;
  }
  
  return false;
}

// 英語用語をアルファベット順に分類
function getEnglishCategory(term) {
  if (!term || term.length === 0) return 'その他';
  
  const firstChar = term.charAt(0).toUpperCase();
  
  // 数字の判定
  if (firstChar >= '0' && firstChar <= '9') {
    return '数字';
  }
  
  // アルファベットの判定
  if (firstChar >= 'A' && firstChar <= 'Z') {
    return firstChar;
  }
  
  return 'その他';
}

index.html(英語専用版)

<!DOCTYPE html>
<html>
<head>
  <base target="_top">
  <script src="https://cdn.tailwindcss.com"></script>
  <style>
    body { 
      font-family: 'Inter', 'Helvetica', 'Arial', sans-serif; 
    }
    .accordion-content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease-out;
    }
    .accordion-item.open .accordion-content {
      max-height: 500px;
    }
    
    .tag-button {
      background-color: #e5e7eb;
      color: #374151;
      font-weight: 600;
      font-size: 0.875rem;
      padding: 0.25rem 0.75rem;
      border-radius: 9999px;
      transition-property: background-color, color;
      transition-duration: 0.2s;
    }
    
    .tag-button:hover {
      background-color: #d1d5db;
    }
    
    .tag-button.active {
      background-color: #3b82f6;
      color: white;
    }
    
    .fixed-nav {
      position: sticky;
      top: 0;
      background-color: white;
      z-index: 10;
      box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
    }
    
    .glossary-container {
      padding-bottom: 50vh;
    }
    
    .english-nav {
      display: flex;
      flex-wrap: wrap;
      gap: 0.5rem;
      justify-content: center;
    }
    
    .english-nav a {
      padding: 0.5rem 0.75rem;
      background-color: #dbeafe;
      border-radius: 0.5rem;
      font-weight: 700;
      color: #1e40af;
      text-decoration: none;
      transition: all 0.2s;
      font-size: 1rem;
      min-width: 2.5rem;
      text-align: center;
    }
    
    .english-nav a:hover {
      background-color: #3b82f6;
      color: white;
      transform: translateY(-1px);
      box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
    }
    
    .english-nav a.special {
      background-color: #fef3c7;
      color: #92400e;
    }
    
    .english-nav a.special:hover {
      background-color: #f59e0b;
      color: white;
    }
  </style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 p-4 sm:p-8">
  <div class="max-w-4xl mx-auto bg-white rounded-xl shadow-lg p-6 sm:p-10">
    <h1 class="text-3xl sm:text-4xl font-bold text-center text-blue-800 mb-2">English Glossary</h1>
    <p class="text-center text-gray-600 mb-8">Search for alphabetic terms and acronyms</p>
    
    <div class="fixed-nav flex flex-col gap-4 py-4 px-6 mx-6 mb-6">
      <!-- 検索ボックス -->
      <div class="relative mb-4">
        <input type="text" id="searchInput" placeholder="Search English terms..." 
               class="w-full px-4 py-2 border-2 border-blue-200 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 pl-10">
        <svg class="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
        </svg>
      </div>
      
      <!-- タグフィルター -->
      <div class="flex flex-wrap gap-2">
        <span class="text-gray-600 mr-2">Tags:</span>
        <div class="flex flex-wrap gap-2">
          <button onclick="filterByTag('all', this)" class="tag-button active">All</button>
          <? for (let i = 0; i < tags.length; i++) { ?>
          <button onclick="filterByTag('<?= tags[i] ?>', this)" class="tag-button"><?= tags[i] ?></button>
          <? } ?>
        </div>
      </div>
      
      <!-- アルファベット専用ナビゲーション -->
      <div class="english-nav">
        <? const alphabetNav = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); ?>
        <? alphabetNav.forEach(letter => { ?>
        <a href="#" onclick="scrollToCategory('<?= letter ?>')"><?= letter ?></a>
        <? }); ?>
        <a href="#" onclick="scrollToCategory('数字')" class="special">0-9</a>
        <a href="#" onclick="scrollToCategory('その他')" class="special">Other</a>
      </div>
    </div>
    
    <!-- 用語リスト -->
    <div id="glossary-container" class="glossary-container">
      <? 
      const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
      const categoryOrder = [...alphabet, '数字', 'その他'];
      categoryOrder.forEach(category => { 
        if (termsData[category]) {
          const terms = termsData[category];
      ?>
      <h2 id="<?= category ?>" class="text-2xl sm:text-3xl font-bold text-blue-600 mt-8 mb-4 pt-16 -mt-16"><?= category ?></h2>
      <? terms.forEach(term => { ?>
      <div class="accordion-item p-4 border border-blue-200 rounded-lg mb-2 cursor-pointer hover:bg-blue-50 transition-colors" onclick="toggleAccordion(this)">
        <div class="flex justify-between items-start mb-2">
          <div class="flex flex-col">
            <div class="flex flex-wrap items-baseline gap-2">
              <span class="text-xl sm:text-2xl font-semibold text-gray-800"><?= term.term ?></span>
              <? if (term.reading) { ?>
              <span class="text-sm text-gray-500"><?= term.reading ?></span>
              <? } ?>
            </div>
          </div>
          <? if (term.tags && term.tags.length > 0) { ?>
          <div class="flex flex-wrap gap-1 ml-2">
            <? term.tags.forEach(tag => { ?>
            <span class="bg-blue-100 text-blue-800 text-xs font-medium px-2.5 py-0.5 rounded-full"><?= tag ?></span>
            <? }); ?>
          </div>
          <? } ?>
        </div>
        <div class="accordion-content mt-2">
          <p class="text-gray-700 leading-relaxed"><?= term.description ?></p>
        </div>
      </div>
      <? }); ?>
      <? } 
      }); ?>
    </div>
  </div>

  <script>
    // [JavaScriptコードは日本語版と同様のため省略]
  </script>
</body>
</html>

💡 2つの専用版を作る場合の手順

  1. script.google.com で「日本語用語集」プロジェクトを作成
  2. 日本語専用のコードを設定・デプロイ
  3. 別プロジェクトとして「English Glossary」を作成
  4. 英語専用のコードを設定・デプロイ
  5. それぞれのURLをGoogle Sitesに埋め込み

🖥️ Google Sitesでの表示パターン

パターン1: 1つのページに統合版

<iframe src="https://script.google.com/macros/s/UNIFIED_APP_ID/exec" 
        width="100%" height="800" frameborder="0">
</iframe>

パターン2: 2つのページに分離

<!-- /glossary-japanese ページ -->
<iframe src="https://script.google.com/macros/s/JAPANESE_APP_ID/exec" 
        width="100%" height="800" frameborder="0">
</iframe>

<!-- /glossary-english ページ -->
<iframe src="https://script.google.com/macros/s/ENGLISH_APP_ID/exec" 
        width="100%" height="800" frameborder="0">
</iframe>

パターン3: 1つのページに2つ並列表示

<div style="display: flex; gap: 20px; flex-wrap: wrap;">
  <div style="flex: 1; min-width: 400px;">
    <h3>🇯🇵 日本語用語集</h3>
    <iframe src="https://script.google.com/macros/s/JAPANESE_APP_ID/exec" 
            width="100%" height="600" frameborder="0">
    </iframe>
  </div>
  <div style="flex: 1; min-width: 400px;">
    <h3>🔤 English Glossary</h3>
    <iframe src="https://script.google.com/macros/s/ENGLISH_APP_ID/exec" 
            width="100%" height="600" frameborder="0">
    </iframe>
  </div>
</div>

企業での活用シナリオ

🏢 IT企業での想定活用例

想定される課題

  • PDF用語集:月1回更新、検索困難
  • 社内WikiやConfluence:アクセスが煩雑
  • 新人教育:用語理解に時間がかかる
  • 部署間の用語認識差

期待される改善

  • スプレッドシート更新: 各部署が随時用語追加可能
  • 企業サイトに統合: 社外からもアクセス可能
  • 検索効率化: 用語検索時間の短縮
  • 新人研修短縮: 用語習得の効率化

🏥 医療機関での想定活用例

カスタマイズポイント

// 診療科別タグ自動付与
function addDepartmentTags(term, description) {
  const departments = {
    '内科': ['血圧', '糖尿病', '高血圧'],
    '外科': ['手術', '麻酔', '切開'],
    '小児科': ['予防接種', '発育', '成長']
  };
  
  for (const [dept, keywords] of Object.entries(departments)) {
    if (keywords.some(keyword => description.includes(keyword))) {
      return dept;
    }
  }
  return '共通';
}

セキュリティ設定

// 病院内IPからのみアクセス許可
function checkHospitalIP() {
  const allowedIPs = ['192.168.1.0/24', '10.0.0.0/16'];
  // IP制限ロジック
}

🏭 製造業での活用例

品質管理用語の特殊表示

<!-- 重要度による色分け -->
<span class="<?= term.importance === 'critical' ? 'bg-red-500 text-white' : 'bg-gray-100' ?>">
  <?= term.term ?>
</span>

運用とメンテナンス

📋 日常的なメンテナンス

1. 用語の追加・編集

手順: スプレッドシートを開く → 新しい行に入力 → 保存
結果: 即座にGoogle Sitesに反映

2. 複数人での編集管理

権限設定:
- 編集者: 各部署の用語集担当者
- 閲覧者: 全社員
- オーナー: IT部門

3. 編集履歴の確認

Google スプレッドシート → 表示 → 変更履歴
→ いつ・誰が・何を変更したかを確認可能

🔧 高度なカスタマイズ

企業ブランドカラーの適用

:root {
  --company-primary: #your-brand-color;
  --company-secondary: #your-secondary-color;
}

.tag-button.active {
  background-color: var(--company-primary);
}

自動バックアップ機能

function createWeeklyBackup() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const backup = ss.copy(`用語集バックアップ_${new Date().toISOString().split('T')[0]}`);
  
  // 専用フォルダに移動
  const folder = DriveApp.getFolderById('BACKUP_FOLDER_ID');
  DriveApp.getFileById(backup.getId()).moveTo(folder);
}

// 毎週日曜日に自動実行
ScriptApp.newTrigger('createWeeklyBackup')
  .timeBased()
  .everyWeeks(1)
  .onWeekDay(ScriptApp.WeekDay.SUNDAY)
  .create();

よくある質問と解決方法

Q1: 「エラーが発生しました」と表示される

A1: よくある原因と解決策

  • スプレッドシートIDが間違っている → URLを再確認
  • シート名が「シート1」と一致しない → Code.gsのSHEET_NAMEを修正
  • Apps Scriptの実行権限がない → 初回実行時に権限を許可

Q2: Google Sitesで表示されない

A2: 確認ポイント

  • WebアプリのURLが正しいか確認
  • アクセス権限が「組織内の全員」になっているか確認
  • iframeのサイズ設定を確認

Q3: スプレッドシートを更新してもサイトに反映されない

A3: 対処方法

  • ブラウザのキャッシュをクリア(Ctrl+F5)
  • Apps Scriptで新しいバージョンをデプロイ
  • しばらく待ってから再度確認

Q4: 社外からアクセスできない

A4: アクセス権限の設定

// 組織内限定の場合
function checkDomain() {
  const user = Session.getActiveUser().getEmail();
  if (!user.endsWith('@yourcompany.com')) {
    return HtmlService.createHtmlOutput('アクセス権限がありません');
  }
}

期待できる効果

📊 導入により期待できる改善

定量的な改善の可能性

  • 用語検索時間の短縮
  • 新人研修期間の短縮
  • 用語関連問い合わせの削減
  • 用語追加・更新頻度の向上

定性的な改善の可能性

  • 新人の学習効率向上
  • 部署間のコミュニケーション改善
  • 顧客対応時の用語説明の統一
  • 社内外への情報発信時の一貫性向上

まとめ

🎯 このシステムの本当の価値

技術的価値

  • Google Workspaceの既存リソースを最大活用
  • コスト0円で企業レベルの用語集システムを構築
  • スプレッドシート→Apps Script→Google Sitesのシームレス連携

業務的価値

  • 用語管理業務の劇的な効率化
  • 新人教育・研修コストの削減
  • 組織内コミュニケーションの円滑化

戦略的価値

  • 企業の知識資産の体系化・活用
  • 情報アクセシビリティの向上
  • デジタル化推進の具体的な成果

🚀 導入を推奨する企業

  • Google Workspaceを使用している企業 → 即座に導入可能
  • 専門用語が多い業界 → IT、医療、金融、製造業など
  • 新人教育を効率化したい企業 → オンボーディング期間短縮
  • 部署間連携を改善したい企業 → 用語の統一で意思疎通向上

📈 期待できる投資対効果

初期コスト: 0円(Googleの無料サービス)
開発工数: 1-2日(技術者1名の場合)
運用コスト: 0円(継続的に無料)

期待される効果:
- 用語検索時間短縮による生産性向上
- 新人研修期間短縮によるコスト削減  
- 問い合わせ対応工数削減
- 情報共有品質向上による業務効率化

💡 最後に

Google Workspaceの標準機能だけで、企業レベルの高機能用語集システムが構築できます。

このシステムは単なる用語集を超えて、企業の知識共有プラットフォームとして機能します。スプレッドシートでのメンテナンス性の良さと、Google Sitesでの高いアクセシビリティを両立した実用的なソリューションです。

ぜひあなたの企業でも、この 「スプレッドシート×Apps Script×Google Sites」 の組み合わせで、用語集システムを構築してみてください!


完全版コード

Google Apps Script(Code.gs)

[上記のCode.jsコード]

HTMLテンプレート(index.html)

[上記のindex.htmlコード]

設定手順チェックリスト

  • スプレッドシート作成・データ入力
  • Apps Scriptプロジェクト作成
  • Code.gs・index.html設定
  • スプレッドシートID更新
  • Webアプリデプロイ・URL取得
  • Google SitesにiFrame埋め込み
  • 動作確認・権限設定

参考リンク

Keywords: Google Sites, スプレッドシート, Apps Script, 企業用語集, Webアプリ, iframe埋め込み, 社内ツール, Google Workspace

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?