소스 정보
- 저장소
- miles990/claude-software-skills
- 최근 소스 활동
- 2026년 1월 8일 02:34
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 20
- 포크
- 5
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/miles990/claude-software-skills --skill internationalization명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
Enterprise-grade repository analysis with arc42/C4 architecture documentation, technical debt quantification, security assessment, and multi-stakeholder reporting
Claude Code Plugin 開發、發布、安裝、更新與 Marketplace 管理完整指南
Flame Engine core fundamentals - components, input, collision, camera, animation, scenes
| schema | 1.0 |
| name | internationalization |
| version | 1.0.0 |
| description | 國際化與本地化開發:i18n/l10n 架構設計、翻譯管理、多語言應用最佳實踐 |
| domain | software-engineering |
| triggers | {"keywords":{"primary":["i18n","l10n","國際化","本地化","internationalization","localization","多語言"],"secondary":["翻譯","translation","locale","language","intl","formatjs","react-intl"]},"context_boost":["multilingual","global","region","currency","date format"],"context_penalty":["backend","database","devops"],"priority":"medium"} |
| author | claude-software-skills |
讓你的應用程式走向全世界
┌─────────────────────────────────────────────────────────────────┐
│ i18n vs l10n │
│ │
│ 國際化 (Internationalization - i18n) │
│ ├─ 設計支援多語言的架構 │
│ ├─ 抽離可翻譯的字串 │
│ ├─ 處理日期、數字、貨幣格式 │
│ └─ 一次性工程工作 │
│ │
│ 本地化 (Localization - l10n) │
│ ├─ 針對特定語言/地區的翻譯 │
│ ├─ 文化適應(圖片、顏色、符號) │
│ ├─ 法規遵循(隱私、內容) │
│ └─ 持續性工作 │
│ │
│ i18n = 讓應用「能」支援多語言 │
│ l10n = 讓應用「實際」支援某語言 │
└─────────────────────────────────────────────────────────────────┘
推薦結構:按語言分類
locales/
├── en/
│ ├── common.json # 共用詞彙
│ ├── home.json # 首頁
│ └── settings.json # 設定頁
├── zh-TW/
│ ├── common.json
│ ├── home.json
│ └── settings.json
└── ja/
├── common.json
├── home.json
└── settings.json
替代結構:按功能分類
locales/
├── common/
│ ├── en.json
│ ├── zh-TW.json
│ └── ja.json
└── settings/
├── en.json
├── zh-TW.json
└── ja.json
## 命名原則
1. **階層式命名**
✅ `settings.notifications.email.title`
❌ `settingsNotificationsEmailTitle`
2. **語意優先於位置**
✅ `action.save`, `action.cancel`
❌ `button1`, `button2`
3. **避免縮寫**
✅ `error.network.timeout`
❌ `err.net.to`
4. **使用英文小寫**
✅ `user.profile.avatar`
❌ `User.Profile.Avatar`
## 常用前綴
| 前綴 | 用途 | 範例 |
|------|------|------|
| `action.` | 動作按鈕 | `action.submit`, `action.delete` |
| `label.` | 表單標籤 | `label.email`, `label.password` |
| `error.` | 錯誤訊息 | `error.required`, `error.invalid` |
| `message.` | 一般訊息 | `message.success`, `message.loading` |
| `title.` | 頁面標題 | `title.home`, `title.settings` |
| `hint.` | 提示文字 | `hint.password_format` |
// i18n.ts - 配置
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en',
supportedLngs: ['en', 'zh-TW', 'ja'],
ns: ['common', 'home', 'settings'],
defaultNS: 'common',
interpolation: {
escapeValue: false,
},
detection: {
order: ['querystring', 'cookie', 'localStorage', 'navigator'],
caches: ['localStorage', 'cookie'],
},
});
// 使用
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t, i18n } = useTranslation();
return (
<div>
{t('title.welcome')}
{t('message.greeting', { name: 'John' })}
i18n.changeLanguage('zh-TW')}>
中文
);
}
// i18n.ts
import { createI18n } from 'vue-i18n';
const i18n = createI18n({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages: {
en: { /* ... */ },
'zh-TW': { /* ... */ },
},
});
// 使用
<template>
<h1>{{ $t('title.welcome') }}</h1>
<p>{{ $t('message.greeting', { name: 'John' }) }}</p>
</template>
<script setup>
import { useI18n } from 'vue-i18n';
const { t, locale } = useI18n();
</script>
// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['en', 'zh-TW', 'ja'],
defaultLocale: 'en',
localePrefix: 'as-needed',
});
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
export default async function LocaleLayout({
children,
params: { locale }
}) {
const messages = await getMessages();
return (
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
);
}
// 使用
import { useTranslations } from 'next-intl';
function Page() {
const t = useTranslations('home');
return <>{t('title')};
}
// en.json
{
"items": {
"zero": "No items",
"one": "{{count}} item",
"other": "{{count}} items"
}
}
// zh-TW.json (中文無複數變化)
{
"items": "{{count}} 個項目"
}
// 使用
t('items', { count: 5 }) // "5 items" / "5 個項目"
{
"greeting": "Hello, {{name}}!",
"date": "Today is {{date, datetime}}",
"price": "Price: {{amount, currency}}",
"percent": "{{value, percent}} complete"
}
// 使用 Intl API
t('date', { date: new Date() });
t('price', { amount: 99.99 });
t('percent', { value: 0.85 });
{
"common": {
"app_name": "MyApp"
},
"welcome": "Welcome to $t(common.app_name)!",
"footer": "© 2024 $t(common.app_name). All rights reserved."
}
// 使用 Intl.DateTimeFormat
const formatDate = (date: Date, locale: string) => {
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date);
};
formatDate(new Date(), 'en-US'); // "January 7, 2024"
formatDate(new Date(), 'zh-TW'); // "2024年1月7日"
formatDate(new Date(), 'ja-JP'); // "2024年1月7日"
// 相對時間
const formatRelative = (date: Date, locale: string) => {
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
const diff = Math.round((date.getTime() - Date.()) / );
rtf.(diff, );
};
(yesterday, );
(yesterday, );
// 數字格式化
const formatNumber = (num: number, locale: string) => {
return new Intl.NumberFormat(locale).format(num);
};
formatNumber(1234567.89, 'en-US'); // "1,234,567.89"
formatNumber(1234567.89, 'de-DE'); // "1.234.567,89"
formatNumber(1234567.89, 'zh-TW'); // "1,234,567.89"
// 貨幣格式化
const formatCurrency = (amount: number, currency: string, locale: string) => {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
}).format(amount);
};
formatCurrency(99.99, 'USD', 'en-US'); // "$99.99"
formatCurrency(99.99, 'TWD', 'zh-TW'); // "NT$99.99"
formatCurrency(, , );
┌─────────────────────────────────────────────────────────────────┐
│ RTL (Right-to-Left) 支援 │
│ │
│ RTL 語言:阿拉伯語、希伯來語、波斯語、烏爾都語 │
│ │
│ HTML 設定 │
│ <html lang="ar" dir="rtl"> │
│ │
│ CSS 邏輯屬性 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ LTR 屬性 → 邏輯屬性 │ │
│ │ margin-left → margin-inline-start │ │
│ │ margin-right → margin-inline-end │ │
│ │ padding-left → padding-inline-start │ │
│ │ text-align:left → text-align: start │ │
│ │ float: left → float: inline-start │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 自動翻轉 │
│ • 圖標方向(箭頭、導航) │
│ • 表單欄位順序 │
│ • 滾動方向 │
└─────────────────────────────────────────────────────────────────┘
/* 使用 CSS 邏輯屬性 */
.card {
margin-inline-start: 1rem;
padding-inline-end: 2rem;
border-inline-start: 3px solid blue;
}
/* 針對 RTL 調整 */
[dir="rtl"] .icon-arrow {
transform: scaleX(-1);
}
/* 使用 :dir() 偽類 */
.nav:dir(rtl) {
flex-direction: row-reverse;
}
┌─────────────────────────────────────────────────────────────────┐
│ 翻譯工作流程 │
│ │
│ 開發者 翻譯人員 │
│ │ │ │
│ │ 1. 新增 key + 預設文字 │ │
│ ├─────────────────────────────→│ │
│ │ │ │
│ │ 2. 同步到翻譯平台 │ │
│ ├─────────────────────────────→│ │
│ │ │ │
│ │ 3. 翻譯 + 審核 │ │
│ │←─────────────────────────────┤ │
│ │ │ │
│ │ 4. 拉取翻譯檔案 │ │
│ ├─────────────────────────────→│ │
│ │ │ │
│ │ 5. 測試 + 發布 │ │
│ ▼ │ │
│ │
│ 工具推薦: │
│ • Lokalise, Crowdin, Phrase │
│ • Weblate (開源) │
│ • POEditor │
└─────────────────────────────────────────────────────────────────┘
## 翻譯 Checklist
### 技術檢查
- [ ] 所有 key 都有翻譯
- [ ] 插值變數正確
- [ ] 複數形式完整
- [ ] 特殊字元正確轉義
- [ ] HTML 標籤完整
### 內容檢查
- [ ] 翻譯準確
- [ ] 語氣一致
- [ ] 專有名詞統一
- [ ] 長度適中(UI 不溢出)
- [ ] 文化適宜
### 自動化測試
```typescript
// 檢查遺漏翻譯
function checkMissingTranslations(
defaultLocale: object,
targetLocale: object,
path: string = ''
) {
const missing: string[] = [];
for (const key in defaultLocale) {
const currentPath = path ? `${path}.${key}` : key;
if (!(key in targetLocale)) {
missing.push(currentPath);
} else if (typeof defaultLocale[key] === 'object') {
missing.push(...checkMissingTranslations(
defaultLocale[key],
targetLocale[key],
currentPath
));
}
}
return missing;
}
| 問題 | 解決方案 |
|---|---|
| 翻譯後文字太長 | 預留空間、使用縮寫、調整 UI |
| 專有名詞不一致 | 建立術語表 (Glossary) |
| 翻譯遺漏 | CI 自動檢查 |
| 上下文不清 | 為翻譯者提供截圖和說明 |
| 硬編碼字串 | ESLint 規則檢查 |
| 動態內容翻譯 | 使用插值,避免字串拼接 |
## 翻譯載入策略
### 1. 按需載入
只載入當前語言,切換時再載入
```typescript
const loadLocale = async (locale: string) => {
const messages = await import(`./locales/${locale}.json`);
i18n.addResourceBundle(locale, 'translation', messages);
};
按頁面/功能分割翻譯檔案
// 只載入首頁需要的翻譯
await i18n.loadNamespaces(['common', 'home']);
// 預載入可能使用的語言
const preloadLocales = ['en', 'zh-TW'];
preloadLocales.forEach(locale => {
i18n.loadLanguages(locale);
});
使用 Service Worker 快取翻譯檔案
## 工具推薦
### 翻譯管理平台
- **Lokalise** - 開發者友好,支援多種格式
- **Crowdin** - 社群翻譯,開源專案免費
- **Phrase** - 企業級,強大的工作流程
### 開發工具
- **i18n Ally** - VS Code 擴展,即時預覽
- **eslint-plugin-i18n** - 檢查硬編碼字串
- **FormatJS** - 強大的格式化工具集
### 測試工具
- **pseudolocalization** - 偽本地化測試
- **i18next-parser** - 自動提取 key
## 最佳實踐總結
```markdown
## DO ✅
1. 從專案開始就考慮 i18n
2. 使用語意化的 key 命名
3. 避免字串拼接,使用插值
4. 為翻譯者提供上下文
5. 自動化翻譯檢查
6. 測試所有支援的語言
## DON'T ❌
1. 硬編碼任何用戶可見文字
2. 假設所有語言長度相同
3. 在 key 中使用預設文字
4. 忽略複數和性別變化
5. 手動管理翻譯同步
6. 忽略 RTL 語言需求