localise について
英語テキストから、コピー機能付きのLokalise向けインタラクティブHTML翻訳ページを生成するスキルです。単数・複数の入力に対応し、翻訳内の補間変数を保持します。
主な機能
- 01翻訳HTMLページの生成
- 02単数形と複数形への対応
- 03補間変数の保持
- 04翻訳文のコピー
- 05対応するMarkdown表の作成
活用シーン
- 01Lokalise用のアプリ文言の準備
- 02複数言語の翻訳確認
- 03翻訳文のローカライズワークフローへのコピー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ morphet81/ cheat-sheets --skill localiseインストール後、Claude Code で/localiseを入力して読み込まれたか確認します。
詳細
Lokalise 翻訳ジェネレーター
Lokalise に翻訳を貼り付けるための、インタラクティブな HTML 翻訳ページを生成します。
使用する場面
- ユーザーが英語の文字列(複数形を含む場合と含まない場合があります)を提示し、Lokalise 用の翻訳を求めている
- ユーザーがアプリの対応言語へのキーの翻訳について言及している
- ユーザーがクリップボードにコピーできる、すぐに使える翻訳リファレンスを必要としている
入力
$ARGUMENTS を翻訳対象の英語テキストとして解析します。形式は <英語の単数形> [| <英語の複数形>] です。
$ARGUMENTSに区切り文字|が含まれる場合は、単数形(|の前)と複数形(|の後)に分割します$ARGUMENTSに|が含まれない場合は、文字列全体をすべての言語の単数形(other)として扱います$ARGUMENTSが空の場合は、AskUserQuestionを使って英語のテキストをユーザーに尋ねます
ユーザーは次の情報も指定できます。
- キー名(任意):例:
tables::selected_count。指定されていない場合は、英語の文字列から導出します。 - 変数:文字列には
%{variable_name}形式の補間トークンが含まれる場合があります。すべての翻訳で、これらをそのまま保持してください。
対象言語(この順序を厳守)
| # | コード | 言語 | 複数形(Lokalise) | RTL |
|---|---|---|---|---|
| 1 | en | 英語 | one, other | いいえ |
| 2 | ar | アラビア語 | zero, one, two, few, many, other | はい |
| 3 | zh-CN | 中国語(簡体字) | other | いいえ |
| 4 | zh-TW | 中国語(繁体字) | other | いいえ |
| 5 | nl | オランダ語 | one, other | いいえ |
| 6 | fr | フランス語 | one, other | いいえ |
| 7 | de | ドイツ語 | one, other | いいえ |
| 8 | he | ヘブライ語 | one, other | はい |
| 9 | hi | ヒンディー語 | one, other | いいえ |
| 10 | id | インドネシア語 | other | いいえ |
| 11 | it | イタリア語 | one, other | いいえ |
| 12 | ja | 日本語 | other | いいえ |
| 13 | km | クメール語 | other | いいえ |
| 14 | ko | 韓国語 | other | いいえ |
| 15 | lo | ラオス語 | other | いいえ |
| 16 | ms | マレー語 | other | いいえ |
| 17 | pt | ポルトガル語 | one, other | いいえ |
| 18 | ru | ロシア語 | one, few, many, other | いいえ |
| 19 | es | スペイン語 | one, other | いいえ |
| 20 | tl | タガログ語 | zero, one, two, few, many, other | いいえ |
| 21 | th | タイ語 | other | いいえ |
| 22 | tr | トルコ語 | one, other | いいえ |
| 23 | vi | ベトナム語 | other | いいえ |
翻訳ガイドライン
- すべての翻訳で、
%{...}補間変数をそのまま保持してください。 otherのみの言語では、単数・複数を区別せず、翻訳を1つだけ指定してください。one/otherの言語では、文法上適切な場合、単数形と複数形を指定してください。言語によって名詞が変化しない場合(例:ヒンディー語、一部の語におけるトルコ語)は、同じ形でもかまいません。- ロシア語(
one/few/many/other)では、ロシア語の複数形の格変化規則に従ってください。 - アラビア語(
zero/one/two/few/many/other)では、アラビア語の複数形規則に従ってください。自然な表現になる場合は、zero形で個数変数を省略してもかまいません(例:「項目が選択されていません」)。 - タガログ語(
zero/one/two/few/many/other)では、タガログ語のリンカー規則に従ってください。自然な表現になる場合は、zero形で個数変数を省略してもかまいません。 - RTL 言語(アラビア語、ヘブライ語)の翻訳行には、
rtlCSS クラスを付けてください。 - レストラン・ホスピタリティ向け UI(TableCheck 製品)にふさわしい、自然な翻訳にしてください。
出力
自己完結型の HTML ファイルを1つ生成します。
ファイルの保存場所
ファイルを次の場所に保存してください:{working_directory}/.tmp/translations-{sanitized_key}.html
{sanitized_key} は、キー名(またはキー名から作成したスラッグ)で、特殊文字をハイフンに置き換えたものです。
生成後、既定のシステムブラウザーでファイルを開いてください:
# macOS
open "{filepath}"
# Linux
xdg-open "{filepath}"
# Try open first, fall back to xdg-open
open "{filepath}" 2>/dev/null || xdg-open "{filepath}"
HTML 構造
生成する HTML は、次の構造に正確に従う必要があります。動的な内容(キー名、翻訳)のみを変更し、以下のテンプレートをそのままコピーしてください。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{KEY_NAME} — Plural Translations</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0e1117;
--surface: #161b22;
--surface-2: #1c2129;
--border: #2d333b;
--border-light: #373e47;
--text: #e6edf3;
--text-muted: #8b949e;
--accent: #58a6ff;
--accent-dim: #1a3a5c;
--green: #3fb950;
--green-dim: #1a3524;
--yellow: #d29922;
--yellow-dim: #3d2e00;
--purple: #bc8cff;
--purple-dim: #2a1a4e;
--orange: #f0883e;
--red: #f85149;
--code-bg: #1a1f27;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: 'DM Sans', sans-serif;
padding: 40px 32px;
line-height: 1.6;
}
.container {
max-width: 1360px;
margin: 0 auto;
}
header { margin-bottom: 36px; }
h1 {
font-size: 22px;
font-weight: 700;
color: var(--text);
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 10px;
}
h1 code {
font-family: 'JetBrains Mono', monospace;
font-size: 16px;
background: var(--accent-dim);
color: var(--accent);
padding: 3px 10px;
border-radius: 6px;
font-weight: 500;
}
.subtitle { color: var(--text-muted); font-size: 14px; }
.legend { display: flex; gap: 20px; margin-top: 14px; flex-wrap: wrap; }
.legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); }
.legend-dot { width: 10px; height: 10px; border-radius: 3px; }
.legend-dot.one { background: var(--green); }
.legend-dot.other { background: var(--accent); }
.legend-dot.few { background: var(--yellow); }
.legend-dot.many { background: var(--purple); }
.legend-dot.two { background: var(--orange); }
.legend-dot.zero { background: var(--red); }
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border: 1px solid var(--border);
border-radius: 10px;
overflow: hidden;
font-size: 14px;
}
thead th {
background: var(--surface-2);
color: var(--text-muted);
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.8px;
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid var(--border);
position: sticky;
top: 0;
z-index: 2;
}
thead th:first-child { width: 50px; }
thead th:nth-child(2) { width: 140px; }
thead th:nth-child(3) { width: 100px; }
tbody tr { transition: background 0.15s; }
tbody tr:hover { background: var(--surface); }
tbody tr:not(:last-child) td { border-bottom: 1px solid var(--border); }
td { padding: 10px 16px; vertical-align: top; }
.lang-code { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 500; color: var(--accent); }
.lang-name { font-weight: 500; color: var(--text); font-size: 13px; }
.lang-name span { color: var(--text-muted); font-weight: 400; font-size: 12px; display: block; }
.form-tag {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-weight: 500;
padding: 2px 8px;
border-radius: 4px;
min-width: 46px;
text-align: center;
flex-shrink: 0;
}
.form-tag.one { background: var(--green-dim); color: var(--green); }
.form-tag.other { background: var(--accent-dim); color: var(--accent); }
.form-tag.few { background: var(--yellow-dim); color: var(--yellow); }
.form-tag.many { background: var(--purple-dim); color: var(--purple); }
.form-tag.two { background: #3d1f00; color: var(--orange); }
.form-tag.zero { background: #3d1116; color: var(--red); }
.translation-cell { display: flex; flex-direction: column; gap: 6px; }
.translation-row {
display: flex;
align-items: center;
gap: 10px;
}
.translation-text {
font-family: 'JetBrains Mono', monospace;
font-size: 13px;
color: var(--text);
flex: 1;
}
.translation-text .var { color: var(--yellow); }
.copy-btn {
flex-shrink: 0;
background: var(--surface-2);
border: 1px solid var(--border);
color: var(--text-muted);
font-family: 'DM Sans', sans-serif;
font-size: 11px;
font-weight: 500;
padding: 4px 10px;
border-radius: 5px;
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.copy-btn:hover {
background: var(--border);
color: var(--text);
border-color: var(--border-light);
}
.copy-btn:active { transform: scale(0.96); }
.copy-btn.copied {
background: var(--green-dim);
border-color: var(--green);
color: var(--green);
}
.copy-btn svg { width: 13px; height: 13px; }
.translation-row.handled {
background: rgba(248, 81, 73, 0.2);
border-radius: 4px;
padding: 2px 6px;
margin: -2px -6px;
}
.rtl .translation-text { direction: rtl; text-align: right; }
footer {
margin-top: 24px;
padding: 16px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
font-size: 12px;
color: var(--text-muted);
line-height: 1.8;
}
footer strong { color: var(--text); font-weight: 600; }
footer code {
font-family: 'JetBrains Mono', monospace;
background: var(--code-bg);
padding: 1px 5px;
border-radius: 3px;
font-size: 11px;
}
.toast {
position: fixed;
bottom: 24px;
right: 24px;
background: var(--green-dim);
border: 1px solid var(--green);
color: var(--green);
font-family: 'DM Sans', sans-serif;
font-size: 13px;
font-weight: 500;
padding: 10px 18px;
border-radius: 8px;
opacity: 0;
transform: translateY(10px);
transition: all 0.25s ease;
pointer-events: none;
z-index: 100;
display: flex;
align-items: center;
gap: 6px;
}
.toast.show { opacity: 1; transform: translateY(0); }
</style>
</head>
<body>
<div class="container">
<header>
<h1>Translations for <code>{KEY_NAME}</code></h1>
<p class="subtitle">23 languages · CLDR plural rules · Variable: <code style="font-family:'JetBrains Mono',monospace;background:var(--yellow-dim);color:var(--yellow);padding:1px 6px;border-radius:4px;font-size:13px">%{count}</code></p>
<div class="legend">
<div class="legend-item"><div class="legend-dot one"></div> one</div>
<div class="legend-item"><div class="legend-dot other"></div> other</div>
<div class="legend-item"><div class="legend-dot few"></div> few</div>
<div class="legend-item"><div class="legend-dot many"></div> many</div>
<div class="legend-item"><div class="legend-dot two"></div> two</div>
<div class="legend-item"><div class="legend-dot zero"></div> zero</div>
</div>
</header>
<table>
<thead>
<tr>
<th>#</th>
<th>Language</th>
<th>Forms</th>
<th>Translations</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<footer>
<strong>Notes:</strong><br>
• These translations should be reviewed by native speakers before production use.<br>
• Plural forms follow CLDR plural rules as configured in Lokalise.<br>
• RTL languages (Arabic, Hebrew) are displayed right-to-left.
</footer>
</div>
<div class="toast" id="toast">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
<span id="toast-text">Copied!</span>
</div>
<script>
const COPY_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>';
const CHECK_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" width="13" height="13"><polyline points="20 6 9 17 4 12"/></svg>';
let toastTimeout;
function copyText(btn) {
const row = btn.closest('.translation-row');
const textEl = row.querySelector('.translation-text');
const text = textEl.getAttribute('data-copy');
navigator.clipboard.writeText(text).then(() => {
showFeedback(btn, text);
}).catch(() => {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;opacity:0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
showFeedback(btn, text);
});
}
function showFeedback(btn, text) {
const row = btn.closest('.translation-row');
row.classList.add('handled');
btn.classList.add('copied');
btn.innerHTML = CHECK_ICON + 'Copied!';
setTimeout(() => {
btn.classList.remove('copied');
btn.innerHTML = COPY_ICON + 'Copy';
}, 1500);
const toast = document.getElementById('toast');
const toastText = document.getElementById('toast-text');
const display = text.length > 45 ? text.substring(0, 45) + '…' : text;
toastText.textContent = 'Copied: ' + display;
clearTimeout(toastTimeout);
toast.classList.add('show');
toastTimeout = setTimeout(() => toast.classList.remove('show'), 2000);
}
</script>
</body>
</html>
行テンプレート
各言語の行を作成するには、以下のテンプレートを使用してください。{N}、{LANG_NAME}、{LANG_CODE}、翻訳文を置き換えてください。
コピー用ボタンの HTML(全箇所で再利用):
<button class="copy-btn" onclick="copyText(this)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>Copy</button>
単一形(other のみ)
対象:zh-CN、zh-TW、id、ja、km、ko、lo、ms、th、vi
<tr>
<td><span class="lang-code">{N}</span></td>
<td><span class="lang-name">{LANG_NAME}<span>{LANG_CODE}</span></span></td>
<td><span class="form-tag other">other</span></td>
<td class="translation-cell">
<div class="translation-row">
<span class="translation-text" data-copy="{PLAIN_TEXT}">{DISPLAY_HTML}</span>
{COPY_BUTTON}
</div>
</td>
</tr>
2つの形(one/other)
対象:en、nl、fr、de、he、hi、it、pt、es、tr
<tr>
<td><span class="lang-code">{N}</span></td>
<td><span class="lang-name">{LANG_NAME}<span>{LANG_CODE}</span></span></td>
<td><span class="form-tag one">one</span> <span class="form-tag other">other</span></td>
<td class="translation-cell">
<div class="translation-row{RTL_CLASS}">
<span class="form-tag one">one</span>
<span class="translation-text" data-copy="{PLAIN_ONE}">{DISPLAY_ONE}</span>
{COPY_BUTTON}
</div>
<div class="translation-row{RTL_CLASS}">
<span class="form-tag other">other</span>
<span class="translation-text" data-copy="{PLAIN_OTHER}">{DISPLAY_OTHER}</span>
{COPY_BUTTON}
</div>
</td>
</tr>
4つの形(one/few/many/other)
対象:ru
<tr>
<td><span class="lang-code">{N}</span></td>
<td><span class="lang-name">Russian<span>ru</span></span></td>
<td><span class="form-tag one">one</span> <span class="form-tag few">few</span> <span class="form-tag many">many</span> <span class="form-tag other">other</span></td>
<td class="translation-cell">
<div class="translation-row">
<span class="form-tag one">one</span>
<span class="translation-text" data-copy="{PLAIN}">{DISPLAY}</span>
{COPY_BUTTON}
</div>
<div class="translation-row">
<span class="form-tag few">few</span>
<span class="translation-text" data-copy="{PLAIN}">{DISPLAY}</span>
{COPY_BUTTON}
</div>
<div class="translation-row">
<span class="form-tag many">many</span>
<span class="translation-text" data-copy="{PLAIN}">{DISPLAY}</span>
{COPY_BUTTON}
</div>
<div class="translation-row">
<span class="form-tag other">other</span>
<span class="translation-text" data-copy="{PLAIN}">{DISPLAY}</span>
{COPY_BUTTON}
</div>
</td>
</tr>
6つの形(zero/one/two/few/many/other)
対象:ar、tl
<tr>
<td><span class="lang-code">{N}</span></td>
<td><span class="lang-name">{LANG_NAME}<span>{LANG_CODE}</span></span></td>
<td><span class="form-tag zero">zero</span> <span class="form-tag one">one</span> <span class="form-tag two">two</span> <span class="form-tag few">few</span> <span class="form-tag many">many</span> <span class="form-tag other">other</span></td>
<td class="translation-cell">
<div class="translation-row{RTL_CLASS}">
<span class="form-tag zero">zero</span>
<span class="translation-text" data-copy="{PLAIN}">{DISPLAY}</span>
{COPY_BUTTON}
</div>
</td>
</tr>
表示 HTML の書式
{DISPLAY} の内容では、%{variable} トークンを黄色のハイライト用 span で囲んでください。
- プレーンテキスト(
data-copy用):%{count} tables selected - 表示 HTML(表示内容用):
<span class="var">%{count}</span> tables selected
Markdown ファイル
HTML ファイルに加えて、すべての翻訳を表形式でまとめた Markdown ファイルも生成してください。
ファイルの保存場所
ファイルを次の場所に保存してください:{working_directory}/.tmp/translations-{sanitized_key}.md
(HTML ファイルと同じディレクトリ、同じファイル名で、拡張子のみ .md にします。)
Markdown の構造
ファイルは次の構造に正確に従う必要があります。
# Translations: `{KEY_NAME}`
| # | Language | Code | Form | Translation |
|---|----------|------|------|-------------|
| 1 | English | en | one | %{count} table selected |
| 1 | English | en | other | %{count} tables selected |
| 2 | Arabic | ar | zero | ... |
| 2 | Arabic | ar | one | ... |
| 2 | Arabic | ar | two | ... |
| 2 | Arabic | ar | few | ... |
| 2 | Arabic | ar | many | ... |
| 2 | Arabic | ar | other | ... |
| 3 | Chinese (Simplified) | zh-CN | other | ... |
...
ルール:
- 言語は HTML と同じ順序(1~23)で並べてください。
- 複数形の各形をそれぞれ別の行にしてください。
#列には、同じ言語のすべての形で同じ行番号を使用してください(例:アラビア語の行はすべて2)。Translation列にはプレーンテキストを記載してください(HTML マークアップを使わず、%{...}変数はそのまま保持します)。- 単一形(
otherのみ)の言語は1行にしてください。 - 複数の形がある言語は、各形を次の順序で個別の行にしてください:zero、one、two、few、many、other(その言語に該当する形のみ)。
保存前のチェックリスト
- 指定された正確な順序ですべての23言語が含まれていること
- 言語ごとの正しい複数形が使われていること(汎用 CLDR ではなく、上記の Lokalise 形式一覧に従う)
- すべての翻訳で
%{...}変数がそのまま保持されていること data-copy属性にプレーンテキストが含まれていること(HTML を含めない)- 表示内容で、変数が
<span class="var">で囲まれていること - アラビア語とヘブライ語の行に RTL クラスが適用されていること
- 行番号が1~23の連番になっていること
- 翻訳のすべての行にコピー用ボタンがあること
- HTML ファイルと Markdown ファイルの両方が
.tmp/フォルダーに保存されていること - HTML ファイルが生成後に既定のブラウザーで開かれていること