L10N Developer Guide

DEVELOPER GUIDE

coForward L10N
다국어 변환 매뉴얼

HTML에 data-l10n 속성만 표시하면 페이지를 새로고침하지 않고 원하는 언어로 바꿔 보여줍니다. 설치, 마킹 규칙, 언어 전환, 딕셔너리, 번역 에디터, 서버 API, 문제 해결까지 서비스에 다국어를 적용하는 데 필요한 내용을 정리했습니다.

01

빠른 시작#

번역할 요소에 data-l10n을 붙이고, 사전과 초기화 스크립트를 불러오면 바로 동작합니다.

최소 예제#

HTML
<!DOCTYPE html>
<html lang="ko">
<body>
  <h1 data-l10n>프로젝트 운영 현황</h1>
  <p data-l10n>배포 일정은 11월 20일로 예정되어 있습니다.</p>

  <!-- 1. 번역 사전 (전역 변수 coForward_l10n_dictionary) -->
  <script src="coForward_l10n_dictionary.js"></script>

  <!-- 2. 뷰어 초기화 -->
  <script src="/api/l10n/coForward_l10n_init.js"
          data-from="ko" data-to="en" type="module"></script>
</body>
</html>

이제 <html>의 lang을 en으로 바꾸면 표시된 문구가 영어로 바뀌고, ko로 되돌리면 원문이 다시 표시됩니다.

동작 원리#

data-l10n 요소 수집→ 원문으로 사전 키 생성→ 사전에서 번역 조회→ 요소 내용·속성 교체
  • <html lang> 변경을 감지해 페이지 전체를 다시 번역합니다.
  • 새로 추가되는 요소와 속성 변경도 자동으로 감지해 번역합니다.
  • 사전에 없는 문구는 원문을 그대로 보여줍니다.
02

설치 및 파일 구성#

방문자에게 번역만 보여주는 뷰어와, 운영자가 화면에서 번역을 등록하는 에디터 중 하나를 선택해 불러옵니다.

파일 위치#

파일용도
/api/l10n/coForward_l10n_init.js뷰어 초기화 (공개 페이지용)
/api/l10n/coForward_l10n_editor_init.js에디터 초기화 (운영자 페이지용)
/api/l10n/module/coForward_l10n.js번역 엔진 클래스
/api/l10n/module/coForward_l10n_editor.js에디터 클래스 (번역 엔진 확장)
/api/l10n/css/에디터·마커 스타일 (Shadow DOM 안에서 로드)
/l10n/dictionary_json.php프로젝트 사전 JSON 다운로드

뷰어 vs 에디터#

구분뷰어에디터
대상일반 방문자번역 운영자
사전 출처전역 변수 coForward_l10n_dictionary서버 API (dictionary.php)
API 키필요 없음data-key 필수
추가 기능—로그인, 마커, 항목 편집, 일괄·직접 등록

뷰어

HTML
<script src="coForward_l10n_dictionary.js"></script>
<script src="/api/l10n/coForward_l10n_init.js"
        data-from="ko" data-to="en" type="module"></script>

에디터

HTML
<script src="/api/l10n/coForward_l10n_editor_init.js"
        data-key="YOUR_API_KEY"
        data-from="ko"
        data-to="en"
        type="module" defer></script>

data-key는 프로젝트의 번역 등록 권한과 연결됩니다. 운영자 전용 페이지에서만 사용하고 공개 페이지에는 넣지 마세요.

03

HTML 마킹#

번역할 요소에 data-l10n 속성을 추가합니다. 값은 비워 두어도 됩니다.

기본 마킹#

HTML
<h1 data-l10n>프로젝트 운영 현황</h1>
<p data-l10n>운영팀은 사용자 매뉴얼 초안을 검토해 주세요.</p>
<label data-l10n for="title">업무 제목</label>

속성 번역#

title, placeholder, alt 속성은 본문과 함께 자동으로 번역됩니다. 본문이 없는 요소는 data-l10n에 고유 이름을 지정하면 사전 키로 사용됩니다.

HTML
<input data-l10n="taskTitle" placeholder="업무 제목을 입력하세요"
       title="프로젝트에 등록할 업무의 제목을 입력합니다.">
<img data-l10n="summaryChart" src="chart.svg"
     alt="이번 주 프로젝트 진행 현황 차트" title="프로젝트 진행 현황">

하위 요소가 있는 문장#

문장 안의 태그는 [[sub-1]], [[sub-2]] 자리표시자로 바뀌어 사전 키가 만들어집니다. 번역문에서 자리표시자 위치를 옮기면 어순이 다른 언어에서도 태그와 스타일이 유지됩니다. 하위 요소의 문구와 속성도 각각 번역됩니다.

HTML
<p data-l10n>배포 일정은 <span>11월 20일</span>로 예정되어 있습니다.</p>
<!-- 사전 키: "배포 일정은 [[sub-1]]로 예정되어 있습니다." -->
<!-- 번역문: "The release is scheduled for [[sub-1]]." -->

줄바꿈#

<br>는 [[br]]로 처리됩니다.

HTML
<p data-l10n>주요 일정입니다.<br>최종 배포는 11월 20일입니다.</p>
<!-- 사전 키: "주요 일정입니다.[[br]]최종 배포는 11월 20일입니다." -->

그룹#

같은 원문이라도 화면 영역에 따라 번역을 다르게 하려면 상위 요소에 data-l10n-group을 지정합니다. 그룹 안의 요소는 그룹 사전에서 먼저 번역을 찾습니다.

HTML
<section data-l10n-group="operation">
  <h1 data-l10n>운영팀 업무 현황</h1>
  <p data-l10n>이번 주 고객 문의 처리 현황을 공유합니다.</p>
</section>

번역 추적 속성#

번역된 요소에는 사전 항목 번호가 자동으로 기록됩니다. 언어를 다시 바꿀 때 이 번호로 원문을 찾아 번역하므로 직접 수정하지 마세요.

속성설명
data-l10n-idx본문 사전 항목 번호
data-l10n-titletitle 속성의 사전 항목 번호
data-l10n-placeholderplaceholder 속성의 사전 항목 번호
data-l10n-altalt 속성의 사전 항목 번호

마커 속성 이름은 설정의 markerName으로 바꿀 수 있습니다. 예를 들어 markerName: 'i18n'이면 data-i18n을 사용합니다.

04

언어 전환#

번역 엔진은 <html> 요소의 lang 속성을 감시합니다. 값만 바꾸면 페이지 전체가 다시 번역됩니다.

JavaScript
document.documentElement.lang = 'en'; // 영어
document.documentElement.lang = 'ja'; // 일본어
document.documentElement.lang = 'ko'; // 원문(한국어)

KO / EN 토글 예시#

버튼의 상태를 aria-checked로 표시하고, 에디터 등 다른 곳에서 언어가 바뀌어도 상태를 맞춥니다.

HTML + JavaScript
<button type="button" class="btn-change-lang" role="switch" aria-checked="false">Change Lang</button>

<script type="module">
  const html = document.documentElement;
  const toggle = document.querySelector('.btn-change-lang');

  toggle.addEventListener('click', () => {
    html.lang = html.lang === 'ko' ? 'en' : 'ko';
  });

  const sync = () => toggle.setAttribute('aria-checked', String(html.lang !== 'ko'));
  new MutationObserver(sync).observe(html, { attributes: true, attributeFilter: ['lang'] });
  sync();
</script>

언어 선택 상자#

HTML
<select onchange="document.documentElement.lang = this.value">
  <option value="ko">한국어</option>
  <option value="en">English</option>
  <option value="ja">日本語</option>
</select>

원문 언어(data-from)와 같은 lang을 지정하면 원문이 표시됩니다. 사전에 해당 언어 번역이 없는 문구도 원문으로 표시됩니다.

05

동적 콘텐츠#

JavaScript로 나중에 추가한 요소도 data-l10n만 있으면 현재 언어로 자동 번역됩니다. 별도 호출이 필요 없습니다.

JavaScript
const item = document.createElement('p');
item.setAttribute('data-l10n', '');
item.textContent = '신규 업무: 사용자 매뉴얼 검토';
document.querySelector('#task-list').appendChild(item); // 추가 즉시 번역

자동 감지 대상#

변경동작
요소 추가data-l10n 요소와 그 하위 요소를 번역
텍스트 변경부모 요소에 data-l10n이 있으면 다시 번역
속성 변경data-l10n, title, placeholder, alt, open, style, lang 변경 시 다시 번역

팝업(<dialog>)처럼 열릴 때 내용이 만들어지는 UI도 같은 방식으로 번역됩니다. 번역 중인 요소에는 coForward-l10n-translating 클래스가 붙으므로 전환 효과를 줄 수 있습니다.

06

딕셔너리 형식#

사전은 원문(사전 키)을 키로, 언어별 번역을 값으로 가지는 JSON 객체입니다. 프로젝트 관리 화면의 사전 JSON 다운로드로 받을 수 있습니다.

기본 형식#

JSON
{
  "프로젝트 운영 현황": {
    "idx": "l10nIdx-1",
    "lang": "ko",
    "translated": { "en": "Project Operations", "ja": "プロジェクト運営状況" }
  },
  "배포 일정은 [[sub-1]]로 예정되어 있습니다.": {
    "idx": "l10nIdx-2",
    "lang": "ko",
    "translated": { "en": "The release is scheduled for [[sub-1]]." }
  }
}

속성 번역#

본문이 없는 요소는 태그명-data-l10n값이 사전 키가 되고, 속성 번역은 속성 이름별로 저장됩니다.

JSON
{
  "INPUT-taskTitle": {
    "idx": "l10nIdx-19",
    "lang": "ko",
    "translated": { "en": "Task title" },
    "placeholder": {
      "업무 제목을 입력하세요": { "translated": { "en": "Enter a task title" } }
    },
    "title": {
      "프로젝트에 등록할 업무의 제목을 입력합니다.": { "translated": { "en": "Enter the title of the task." } }
    }
  }
}

그룹 형식#

JSON
{
  "operation": {
    "운영팀 업무 현황": {
      "idx": "l10nIdx-38",
      "lang": "ko",
      "translated": { "en": "Operations Team Status" }
    }
  }
}

필드 설명#

필드타입설명
idxstring사전 항목 고유 번호. 요소의 data-l10n-idx에 기록됩니다.
langstring원문 언어 코드
translatedobject언어 코드 → 번역문
title / placeholder / altobject속성 원문 → { translated }

PHP로 사전 출력#

PHP + HTML
<?php
$l10n = new L10N();
$dict = $l10n->make_dictionary($project_idx);
?>
<script>
  var coForward_l10n_dictionary = <?php echo json_encode($dict, JSON_UNESCAPED_UNICODE); ?>;
</script>
07

번역 에디터#

운영자가 실제 화면을 보면서 원문을 등록하고 번역을 입력하는 도구입니다. 화면 우측 상단에 고정된 패널로 표시됩니다.

스크립트 속성#

속성필수설명
data-key예프로젝트 다국어 API 키 (프로젝트 설정의 l10n 값)
data-from아니요원문 언어. <html lang>이 있으면 그 값을 우선 사용 (기본 ko)
data-to아니요처음 선택할 번역 언어 (기본 en)

초기화 흐름#

validate_key.php→ projectIdx 확인→ dictionary.php→ 페이지 번역→ 에디터 UI 표시

운영자 사용 순서#

  1. 로그인프로젝트 계정으로 로그인합니다. 로그인 정보는 24시간 동안 브라우저에 유지됩니다.
  2. 일괄등록현재 화면의 모든 data-l10n 문구를 원문으로 한 번에 등록합니다.
  3. 항목 편집번역할 문구의 마커를 눌러 언어별 번역문을 입력하고 저장합니다. 저장하면 화면에 바로 반영됩니다.
  4. 직접등록화면에 없는 문구(알림 메시지 등)는 원문과 번역문을 직접 입력해 등록합니다.
  5. 확인번역 언어를 선택하고 번역보기로 결과를 확인합니다. 원본보기를 누르면 원문으로 돌아갑니다.

에디터 기능#

기능설명
To (언어 선택)번역 대상 언어 선택 (영어, 일본어, 중국어 간체·번체)
원본보기 / 번역보기<html lang>을 원문 언어 또는 선택한 언어로 변경
마커숨김번역 대상 요소 위의 편집 마커 표시 전환
일괄등록화면의 번역 대상 문구를 사전 원문으로 일괄 등록
직접등록원문과 번역문을 직접 입력해 등록

페이지 스타일과의 충돌#

에디터는 <section id="coForward-l10n-editor">로 추가되고 내부는 Shadow DOM으로 분리됩니다. 페이지에 section 전체에 적용되는 스타일이 있으면 에디터 위치가 흐트러질 수 있으니 제외해 주세요.

CSS
section:not(#coForward-l10n-editor){
  max-width:960px;
  margin:28px auto;
  padding:36px 40px;
}
08

서버 API#

모든 엔드포인트는 /api/l10n/ 아래에 있으며, JSON 본문을 POST로 보내고 JSON으로 응답합니다.

엔드포인트요청설명
validate_key.php{ key }API 키 검증. { valid, projectIdx } 반환
dictionary.php{ projectIdx }프로젝트 사전 전체 반환
reg.php원문·번역 정보번역 항목 등록
reg_headword.php원문 목록원문(사전 키) 일괄 등록
reg_translation.php번역문 목록번역문 일괄 등록
check_headword.php원문원문 중복 확인
delete_headword.php항목 번호원문과 연결된 번역 삭제
JavaScript
const res = await fetch('/api/l10n/validate_key.php', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ key: 'YOUR_API_KEY' })
});
const { valid, projectIdx } = await res.json();
09

문제 해결#

개발자 도구 콘솔과 Network 탭에서 validate_key.php, dictionary.php 응답을 먼저 확인하세요.

Unexpected token '<' … is not valid JSON#

API가 JSON 대신 HTML(오류 메시지)을 보냈다는 뜻입니다.

  • 응답이 <hr />[CLASS_DB] … DB연결이 실패함으로 시작하면 DB 연결 설정을 확인합니다.
  • 응답이 <br /><b>Warning</b>로 시작하면 PHP 경고가 섞인 것입니다. 예: 같은 상수를 두 번 define한 경우.
  • 로컬 개발 설정은 접속 주소의 호스트 이름(HTTP_HOST)으로 판별합니다. localhost:8000처럼 포트가 붙으면 로컬로 인식되지 않을 수 있습니다.

Invalid API key for coForward_l10n_editor#

API는 정상 응답했지만 {"valid":false}인 경우입니다. 페이지의 data-key와 연결된 DB의 프로젝트 l10n 값이 정확히 같은지 확인하세요. 다른 DB를 바라보고 있어도 발생합니다.

콘솔에 "사전을 찾지 못함!!!"#

요소에 기록된 data-l10n-idx 번호를 현재 사전에서 찾지 못했다는 뜻입니다. 사전 로딩이 실패했거나, 사전이 갱신되어 번호가 바뀐 경우입니다. 페이지를 새로고침하거나 사전 로딩 오류부터 해결하세요.

속성값이 "false"로 표시됨#

사전에 없는 속성(placeholder, alt 등)이 있는 상태에서 언어를 여러 번 바꾸면 속성값이 false로 바뀔 수 있습니다. 해당 문구를 사전에 등록하면 나타나지 않습니다.

사전 로딩에 실패한 상태에서 언어를 전환하면 모든 속성에서 이 현상이 생길 수 있습니다. 먼저 사전이 정상적으로 로드되는지 확인하세요.

번역이 전혀 적용되지 않음#

  • 요소에 data-l10n이 있는지 확인합니다.
  • 뷰어는 coForward_l10n_dictionary 전역 변수가 초기화 스크립트보다 먼저 선언되어야 합니다.
  • 사전 키는 공백과 특수문자까지 원문과 정확히 같아야 합니다.
  • 그룹 안의 요소는 해당 그룹 사전에 번역이 있어야 합니다.
10

JavaScript 레퍼런스#

번역 엔진을 직접 생성해 사용할 때 참고합니다.

JavaScript
import coForward_l10n from '/api/l10n/module/coForward_l10n.js';

const l10n = new coForward_l10n({
  from: 'ko',
  to: 'en',
  markerName: 'l10n',
  dictionary: coForward_l10n_dictionary
});

생성자 설정#

키기본값설명
from'ko'원문 언어
to'en'기본 번역 언어
markerName'l10n'마킹 속성 이름 (data-{markerName})
dictionary{}사전 객체 또는 사전 URL

메서드#

메서드설명
setDictionary(dictionary)사전 설정. 객체면 바로 적용하고, 문자열이면 URL로 불러옴
setDictionaryFromUrl(url)서버에서 사전을 받아 적용한 뒤 현재 언어로 번역
translatePageByLang(lang)페이지의 모든 마킹 요소를 지정 언어로 번역 (현재 언어와 같으면 무시)
translateElement(el, lang)요소 하나와 하위 요소를 번역
getTranslatedString(keyText, lang, group)사전 키의 번역 조회. { idx, translated } 반환
makeKeyText(el)요소의 사전 키, 하위 요소, 번역 대상 속성 추출
keyTextByIdx(idx, group)항목 번호로 사전 키 조회
setFormLang(lang) / setToLang(lang)원문 언어 / 번역 언어 변경

에디터 클래스#

coForward_l10n_editor는 번역 엔진을 확장하며 다음 설정을 추가로 받습니다. 보통은 coForward_l10n_editor_init.js가 자동으로 생성합니다.

키설명
key프로젝트 API 키 (필수)
urlAPI 서버 기준 URL (URL 객체, 필수)
targetLangs번역 대상 언어 목록. 기본 ['en','ja','zh-CN','zh-TW']