增加自动翻译skill

This commit is contained in:
wdjwxh 2026-03-22 11:12:35 +08:00
parent 40f123d4e4
commit c3e28bf7de
9 changed files with 1615 additions and 20 deletions

View File

@ -3,7 +3,12 @@
"allow": [
"Bash(python:*)",
"Bash(tree:*)",
"Bash(dir:*)"
"Bash(dir:*)",
"Bash(python3 -m venv venv)",
"Bash(source venv/bin/activate)",
"Bash(pip install:*)",
"Bash(source /mnt/d/code/sync-pd2-wiki/venv/bin/activate)",
"Bash(curl -s \"https://wiki.projectdiablo2.cn/w/api.php?action=query&prop=revisions&titles=Filter%20Info&rvprop=ids&format=json\")"
]
}
}

View File

@ -0,0 +1,148 @@
---
name: mediawiki-wikitext
description: MediaWiki Wikitext markup language for Wikipedia and wiki-based sites. Use when creating or editing wiki articles, generating wikitext content, working with wiki tables/templates/references, or converting content to wikitext format. Triggers on requests mentioning Wikipedia, MediaWiki, wikitext, wiki markup, or wiki article creation.
---
# MediaWiki Wikitext
Generate and edit content using MediaWiki's wikitext markup language.
## Quick Reference
### Text Formatting
```wikitext
''italic'' '''bold''' '''''bold italic'''''
<code>inline code</code> <sub>subscript</sub> <sup>superscript</sup>
<s>strikethrough</s> <u>underline</u>
```
### Headings (line start only, avoid level 1)
```wikitext
== Level 2 ==
=== Level 3 ===
==== Level 4 ====
```
### Lists
```wikitext
* Bullet item # Numbered item ; Term
** Nested ## Nested : Definition
```
### Links
```wikitext
[[Page Name]] Internal link
[[Page Name|Display Text]] With display text
[[Page Name#Section]] Section link
[https://url Display Text] External link
[[File:image.jpg|thumb|Caption]] Image
[[Category:Name]] Category (place at end)
```
### Table
```wikitext
{| class="wikitable"
|+ Caption
|-
! Header 1 !! Header 2
|-
| Cell 1 || Cell 2
|}
```
### Templates & Variables
```wikitext
{{TemplateName}} Basic call
{{TemplateName|arg1|name=value}} With arguments
{{{parameter|default}}} Parameter (in template)
{{PAGENAME}} {{CURRENTYEAR}} Magic words
```
### References
```wikitext
Text<ref>Citation here</ref>
<ref name="id">Citation</ref> Named reference
<ref name="id" /> Reuse reference
{{Reflist}} Display footnotes
```
### Special Tags
```wikitext
<nowiki>[[escaped]]</nowiki> Disable markup
<pre>preformatted block</pre> Preformatted (no markup)
<syntaxhighlight lang="python"> Code highlighting
code here
</syntaxhighlight>
<math>x^2 + y^2 = z^2</math> LaTeX math
<!-- comment --> Comment (hidden)
---- Horizontal rule
#REDIRECT [[Target Page]] Redirect (first line only)
```
### Magic Words
```wikitext
__NOTOC__ Hide table of contents
__TOC__ Position TOC here
__NOEDITSECTION__ Hide section edit links
```
## Common Patterns
### Article Structure
```wikitext
{{Infobox Type
| name = Example
| image = Example.jpg
}}
'''Article Title''' is a brief introduction.
== Section ==
Content with citation<ref>Source</ref>.
=== Subsection ===
More content.
== See also ==
* [[Related Article]]
== References ==
{{Reflist}}
== External links ==
* [https://example.com Official site]
{{DEFAULTSORT:Sort Key}}
[[Category:Category Name]]
```
### Template Definition
```wikitext
<noinclude>{{Documentation}}</noinclude><includeonly>
{| class="wikitable"
! {{{title|Default Title}}}
|-
| {{{content|No content provided}}}
{{#if:{{{footer|}}}|
{{!}}-
{{!}} {{{footer}}}
}}
|}
</includeonly>
```
## Key Syntax Rules
1. **Headings**: Use `==` to `======`; don't use `=` (reserved for page title)
2. **Line-start markup**: Lists (`*#;:`), headings, tables (`{|`) must start at line beginning
3. **Closing tags**: Close heading equals on same line; no text after closing `==`
4. **Blank lines**: Create paragraph breaks; single newlines are ignored
5. **Pipes in templates**: Use `{{!}}` for literal `|` inside templates
6. **Escaping**: Use `<nowiki>` to escape markup; `&amp;` for `&`, `&lt;` for `<`
## Resources
For detailed syntax, see:
- **references/syntax.md**: Complete markup reference with all options
- **references/templates.md**: Template and parser function details
- **assets/snippets.yaml**: Editor snippets for common patterns

View File

@ -0,0 +1,236 @@
# MediaWiki Wikitext Snippets
# For VS Code and compatible editors
---
# Headings
h2:
prefix: '@h2'
body: "== ${1:Heading} ==\n"
description: Level 2 heading
h3:
prefix: '@h3'
body: "=== ${1:Heading} ===\n"
description: Level 3 heading
h4:
prefix: '@h4'
body: "==== ${1:Heading} ====\n"
description: Level 4 heading
# Text formatting
bold:
prefix: '@bold'
body: "'''${1:text}'''"
description: Bold text
italic:
prefix: '@italic'
body: "''${1:text}''"
description: Italic text
# Links
link:
prefix: '@link'
body: '[[${1:Page}]]'
description: Internal link
linkd:
prefix: '@linkd'
body: '[[${1:Page}|${2:Display}]]'
description: Link with display text
elink:
prefix: '@elink'
body: '[${1:https://} ${2:Text}]'
description: External link
file:
prefix: '@file'
body: '[[File:${1:name.jpg}|thumb|${2:Caption}]]'
description: Image/file
cat:
prefix: '@cat'
body: '[[Category:${1:Name}]]'
description: Category
# Table
table:
prefix: '@table'
body: |
{| class="wikitable"
|+ ${1:Caption}
|-
! ${2:Header1} !! ${3:Header2}
|-
| ${4:Cell1} || ${5:Cell2}
|}
description: Basic table
tr:
prefix: '@tr'
body: |
|-
| ${1} || ${2}
description: Table row
# References
ref:
prefix: '@ref'
body: '<ref>${1:Citation}</ref>'
description: Reference
refn:
prefix: '@refn'
body: '<ref name="${1:id}">${2:Citation}</ref>'
description: Named reference
refr:
prefix: '@refr'
body: '<ref name="${1:id}" />'
description: Reference reuse
reflist:
prefix: '@reflist'
body: |
== References ==
{{Reflist}}
description: References section
# Templates
tpl:
prefix: '@tpl'
body: '{{${1:Template}}}'
description: Template call
tplp:
prefix: '@tplp'
body: '{{${1:Template}|${2:param}=${3:value}}}'
description: Template with params
infobox:
prefix: '@infobox'
body: |
{{Infobox ${1:type}
| name = ${2}
| image = ${3}
}}
description: Infobox
# Code
code:
prefix: '@code'
body: |
<syntaxhighlight lang="${1:python}">
${2:code}
</syntaxhighlight>
description: Syntax highlight block
codei:
prefix: '@codei'
body: '<code>${1:code}</code>'
description: Inline code
nowiki:
prefix: '@nowiki'
body: '<nowiki>${1:text}</nowiki>'
description: Escape markup
pre:
prefix: '@pre'
body: |
<pre>
${1:text}
</pre>
description: Preformatted block
math:
prefix: '@math'
body: '<math>${1:formula}</math>'
description: Math formula
# Comments
comment:
prefix: '@comment'
body: '<!-- ${1:comment} -->'
description: Comment
todo:
prefix: '@todo'
body: '<!-- TODO: ${1:task} -->'
description: TODO comment
# Magic words
notoc:
prefix: '@notoc'
body: '__NOTOC__'
description: Hide TOC
toc:
prefix: '@toc'
body: '__TOC__'
description: TOC position
# Lists
ul:
prefix: '@ul'
body: |
* ${1:Item 1}
* ${2:Item 2}
* ${3:Item 3}
description: Bullet list
ol:
prefix: '@ol'
body: |
# ${1:Item 1}
# ${2:Item 2}
# ${3:Item 3}
description: Numbered list
dl:
prefix: '@dl'
body: |
; ${1:Term}
: ${2:Definition}
description: Definition list
# Structure
redirect:
prefix: '@redirect'
body: '#REDIRECT [[${1:Target}]]'
description: Redirect
article:
prefix: '@article'
body: |
{{Infobox
| name = ${1:Name}
}}
'''${2:Title}''' is ${3:description}.
== Overview ==
${4:Content}
== References ==
{{Reflist}}
[[Category:${5:Category}]]
description: Article template
hr:
prefix: '@hr'
body: '----'
description: Horizontal rule
br:
prefix: '@br'
body: '<br />'
description: Line break
sort:
prefix: '@sort'
body: '{{DEFAULTSORT:${1:Key}}}'
description: Default sort key

View File

@ -0,0 +1,345 @@
# MediaWiki Wikitext Syntax Reference
Complete syntax reference for MediaWiki wikitext markup.
## Text Formatting (Inline)
| Syntax | Result | Notes |
|--------|--------|-------|
| `''text''` | *italic* | Two single quotes |
| `'''text'''` | **bold** | Three single quotes |
| `'''''text'''''` | ***bold italic*** | Five single quotes |
| `<code>text</code>` | `monospace` | Inline code |
| `<var>x</var>` | Variable style | |
| `<kbd>Ctrl</kbd>` | Keyboard input | |
| `<samp>output</samp>` | Sample output | |
| `<sub>2</sub>` | Subscript | H₂O |
| `<sup>2</sup>` | Superscript | x² |
| `<s>text</s>` | ~~strikethrough~~ | |
| `<del>text</del>` | Deleted text | Semantic |
| `<ins>text</ins>` | Inserted text | Often underlined |
| `<u>text</u>` | Underline | |
| `<small>text</small>` | Small text | |
| `<big>text</big>` | Large text | Deprecated |
## Headings
```wikitext
= Level 1 = (Don't use - reserved for page title)
== Level 2 ==
=== Level 3 ===
==== Level 4 ====
===== Level 5 =====
====== Level 6 ======
```
**Rules:**
- Must start at line beginning
- No text after closing equals on same line
- 4+ headings auto-generate TOC (unless `__NOTOC__`)
- Spaces around heading text are optional but recommended
## Lists
### Unordered (Bullet) Lists
```wikitext
* Item 1
* Item 2
** Nested item 2.1
** Nested item 2.2
*** Deeper nesting
* Item 3
```
### Ordered (Numbered) Lists
```wikitext
# First item
# Second item
## Sub-item 2.1
## Sub-item 2.2
### Sub-sub-item
# Third item
```
### Definition Lists
```wikitext
; Term 1
: Definition 1
; Term 2
: Definition 2a
: Definition 2b
```
### Mixed Lists
```wikitext
# Numbered item
#* Bullet under numbered
#* Another bullet
# Next numbered
#: Definition-style continuation
```
### Indentation
```wikitext
: Single indent
:: Double indent
::: Triple indent
```
**Note:** List markers must be at line start. Blank lines end the list.
## Links
### Internal Links
```wikitext
[[Page Name]]
[[Page Name|Display Text]]
[[Page Name#Section]]
[[Page Name#Section|Display Text]]
[[Namespace:Page Name]]
[[/Subpage]]
[[../Sibling Page]]
```
### External Links
```wikitext
[https://example.com] Numbered link [1]
[https://example.com Display Text] Named link
https://example.com Auto-linked
```
### Special Links
```wikitext
[[File:Image.jpg]] Embed image
[[File:Image.jpg|thumb|Caption]] Thumbnail with caption
[[File:Image.jpg|thumb|left|200px|Caption]]
[[Media:File.pdf]] Direct file link
[[Category:Category Name]] Add to category
[[:Category:Category Name]] Link to category (no add)
[[Special:RecentChanges]] Special page
```
### Interwiki Links
```wikitext
[[en:English Article]] Language link
[[wikt:word]] Wiktionary
[[commons:File:Image.jpg]] Wikimedia Commons
```
## Images
### Basic Syntax
```wikitext
[[File:Example.jpg|options|caption]]
```
### Image Options
| Option | Description |
|--------|-------------|
| `thumb` | Thumbnail (default right-aligned) |
| `frame` | Framed, no resize |
| `frameless` | Thumbnail without frame |
| `border` | Thin border |
| `right`, `left`, `center`, `none` | Alignment |
| `200px` | Width |
| `x100px` | Height |
| `200x100px` | Max dimensions |
| `upright` | Smart scaling for tall images |
| `upright=0.5` | Custom ratio |
| `link=Page` | Custom link target |
| `link=` | No link |
| `alt=Text` | Alt text for accessibility |
### Gallery
```wikitext
<gallery>
File:Image1.jpg|Caption 1
File:Image2.jpg|Caption 2
</gallery>
<gallery mode="packed" heights="150">
File:Image1.jpg
File:Image2.jpg
</gallery>
```
## Tables
### Basic Structure
```wikitext
{| class="wikitable"
|+ Caption
|-
! Header 1 !! Header 2 !! Header 3
|-
| Cell 1 || Cell 2 || Cell 3
|-
| Cell 4 || Cell 5 || Cell 6
|}
```
### Table Elements
| Markup | Location | Meaning |
|--------|----------|---------|
| `{|` | Start | Table start |
| `|}` | End | Table end |
| `|+` | After `{|` | Caption |
| `|-` | Row | Row separator |
| `!` | Cell | Header cell |
| `!!` | Cell | Header cell separator (same row) |
| `|` | Cell | Data cell |
| `||` | Cell | Data cell separator (same row) |
### Cell Attributes
```wikitext
| style="background:#fcc" | Red background
| colspan="2" | Spans 2 columns
| rowspan="3" | Spans 3 rows
! scope="col" | Column header
! scope="row" | Row header
```
### Sortable Table
```wikitext
{| class="wikitable sortable"
|-
! Name !! Value
|-
| Alpha || 1
| Beta || 2
|}
```
## References
### Basic Citation
```wikitext
Statement<ref>Source information</ref>
== References ==
{{Reflist}}
```
### Named References
```wikitext
First use<ref name="smith2020">Smith, 2020, p. 42</ref>
Second use<ref name="smith2020" />
```
### Grouped References
```wikitext
Note<ref group="note">Explanatory note</ref>
Source<ref>Regular citation</ref>
== Notes ==
{{Reflist|group="note"}}
== References ==
{{Reflist}}
```
## Special Tags
### nowiki (Escape Markup)
```wikitext
<nowiki>[[Not a link]]</nowiki>
<<nowiki/>nowiki> Outputs: <nowiki>
```
### pre (Preformatted)
```wikitext
<pre>
Preformatted text
Whitespace preserved
'''Markup not processed'''
</pre>
```
### syntaxhighlight (Code)
```wikitext
<syntaxhighlight lang="python">
def hello():
print("Hello")
</syntaxhighlight>
<syntaxhighlight lang="python" line="1" start="10">
# Line numbers starting at 10
</syntaxhighlight>
```
Supported languages: python, javascript, php, java, c, cpp, csharp, ruby, perl, sql, xml, html, css, json, yaml, bash, etc.
### math (LaTeX)
```wikitext
Inline: <math>E = mc^2</math>
Block: <math display="block">\sum_{i=1}^n i = \frac{n(n+1)}{2}</math>
Chemistry: <chem>H2O</chem>
```
### Transclusion Control
```wikitext
<includeonly>Only when transcluded</includeonly>
<noinclude>Only on template page itself</noinclude>
<onlyinclude>Only this part is transcluded</onlyinclude>
```
## HTML Entities
| Entity | Character | Description |
|--------|-----------|-------------|
| `&amp;` | & | Ampersand |
| `&lt;` | < | Less than |
| `&gt;` | > | Greater than |
| `&nbsp;` | (space) | Non-breaking space |
| `&mdash;` | — | Em dash |
| `&ndash;` | | En dash |
| `&rarr;` | → | Right arrow |
| `&larr;` | ← | Left arrow |
| `&copy;` | © | Copyright |
| `&euro;` | € | Euro |
| `&#58;` | : | Colon (in definition lists) |
## Miscellaneous
### Horizontal Rule
```wikitext
----
```
### Comments
```wikitext
<!-- This is a comment -->
<!--
Multi-line
comment
-->
```
### Line Breaks
```wikitext
Line 1<br />Line 2
```
### Redirect
```wikitext
#REDIRECT [[Target Page]]
#REDIRECT [[Target Page#Section]]
```
Must be first line of page.
### Signatures (Talk pages)
```wikitext
~~~ Username only
~~~~ Username and timestamp
~~~~~ Timestamp only
```
### Categories
```wikitext
[[Category:Category Name]]
[[Category:Category Name|Sort Key]]
{{DEFAULTSORT:Sort Key}}
```
Place at end of article.

View File

@ -0,0 +1,311 @@
# MediaWiki Templates and Parser Functions
## Template Basics
### Calling Templates
```wikitext
{{TemplateName}}
{{TemplateName|positional arg}}
{{TemplateName|param1=value1|param2=value2}}
{{TemplateName
| param1 = value1
| param2 = value2
}}
```
### Template Parameters (Definition Side)
```wikitext
{{{1}}} First positional parameter
{{{paramName}}} Named parameter
{{{1|default}}} With default value
{{{paramName|}}} Empty default (vs undefined)
```
### Transclusion
```wikitext
{{:Page Name}} Transclude article (with colon)
{{Template Name}} Transclude template
{{subst:Template Name}} Substitute (one-time expansion)
{{safesubst:Template}} Safe substitution
{{msgnw:Template}} Show raw wikitext
```
## Parser Functions
### Conditionals
#### #if (empty test)
```wikitext
{{#if: {{{param|}}} | not empty | empty or undefined }}
{{#if: {{{param|}}} | has value }}
```
#### #ifeq (equality test)
```wikitext
{{#ifeq: {{{type}}} | book | It's a book | Not a book }}
{{#ifeq: {{{1}}} | {{{2}}} | same | different }}
```
#### #iferror
```wikitext
{{#iferror: {{#expr: 1/0}} | Division error | OK }}
```
#### #ifexist (page exists)
```wikitext
{{#ifexist: Page Name | [[Page Name]] | Page doesn't exist }}
```
#### #ifexpr (expression test)
```wikitext
{{#ifexpr: {{{count}}} > 10 | Many | Few }}
{{#ifexpr: {{{year}}} mod 4 = 0 | Leap year candidate }}
```
#### #switch
```wikitext
{{#switch: {{{type}}}
| book = 📚 Book
| article = 📄 Article
| website = 🌐 Website
| #default = 📋 Other
}}
{{#switch: {{{1}}}
| A | B | C = First three letters
| #default = Something else
}}
```
### String Functions
#### #len
```wikitext
{{#len: Hello }} Returns: 5
```
#### #pos (find position)
```wikitext
{{#pos: Hello World | o }} Returns: 4 (first 'o')
{{#pos: Hello World | o | 5 }} Returns: 7 (after position 5)
```
#### #sub (substring)
```wikitext
{{#sub: Hello World | 0 | 5 }} Returns: Hello
{{#sub: Hello World | 6 }} Returns: World
{{#sub: Hello World | -5 }} Returns: World (from end)
```
#### #replace
```wikitext
{{#replace: Hello World | World | Universe }} Returns: Hello Universe
```
#### #explode (split)
```wikitext
{{#explode: a,b,c,d | , | 2 }} Returns: c (third element)
```
#### #urlencode / #urldecode
```wikitext
{{#urlencode: Hello World }} Returns: Hello%20World
{{#urldecode: Hello%20World }} Returns: Hello World
```
### Math Functions
#### #expr
```wikitext
{{#expr: 1 + 2 * 3 }} Returns: 7
{{#expr: (1 + 2) * 3 }} Returns: 9
{{#expr: 2 ^ 10 }} Returns: 1024
{{#expr: 17 mod 5 }} Returns: 2
{{#expr: floor(3.7) }} Returns: 3
{{#expr: ceil(3.2) }} Returns: 4
{{#expr: round(3.567, 2) }} Returns: 3.57
{{#expr: abs(-5) }} Returns: 5
{{#expr: sqrt(16) }} Returns: 4
{{#expr: ln(e) }} Returns: 1
{{#expr: sin(pi/2) }} Returns: 1
```
**Operators:** `+`, `-`, `*`, `/`, `^` (power), `mod`, `round`, `floor`, `ceil`, `abs`, `sqrt`, `ln`, `exp`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `pi`, `e`
**Comparison:** `=`, `<>`, `!=`, `<`, `>`, `<=`, `>=`
**Logical:** `and`, `or`, `not`
### Date/Time Functions
#### #time
```wikitext
{{#time: Y-m-d }} Current: 2024-01-15
{{#time: F j, Y | 2024-01-15 }} January 15, 2024
{{#time: Y年n月j日 | 2024-01-15 }} 2024年1月15日
{{#time: l | 2024-01-15 }} Monday
```
**Format codes:**
| Code | Output | Description |
|------|--------|-------------|
| Y | 2024 | 4-digit year |
| y | 24 | 2-digit year |
| n | 1 | Month (no leading zero) |
| m | 01 | Month (with leading zero) |
| F | January | Full month name |
| M | Jan | Abbreviated month |
| j | 5 | Day (no leading zero) |
| d | 05 | Day (with leading zero) |
| l | Monday | Full weekday |
| D | Mon | Abbreviated weekday |
| H | 14 | Hour (24h, leading zero) |
| i | 05 | Minutes (leading zero) |
| s | 09 | Seconds (leading zero) |
#### #timel (local time)
```wikitext
{{#timel: H:i }} Local time
```
### Formatting Functions
#### #formatnum
```wikitext
{{#formatnum: 1234567.89 }} 1,234,567.89
{{#formatnum: 1,234.56 | R }} 1234.56 (raw)
```
#### #padleft / #padright
```wikitext
{{#padleft: 7 | 3 | 0 }} 007
{{#padright: abc | 6 | . }} abc...
```
#### #lc / #uc / #lcfirst / #ucfirst
```wikitext
{{#lc: HELLO }} hello
{{#uc: hello }} HELLO
{{#lcfirst: HELLO }} hELLO
{{#ucfirst: hello }} Hello
{{lc: HELLO }} hello (shortcut)
```
### Other Functions
#### #tag
```wikitext
{{#tag: ref | Citation text | name=smith }}
Equivalent to: <ref name="smith">Citation text</ref>
```
#### #invoke (Lua modules)
```wikitext
{{#invoke: ModuleName | functionName | arg1 | arg2 }}
```
## Magic Words
### Behavior Switches
```wikitext
__NOTOC__ No table of contents
__FORCETOC__ Force TOC even with <4 headings
__TOC__ Place TOC here
__NOEDITSECTION__ No section edit links
__NEWSECTIONLINK__ Add new section link
__NONEWSECTIONLINK__ Remove new section link
__NOGALLERY__ No gallery in category
__HIDDENCAT__ Hidden category
__INDEX__ Index by search engines
__NOINDEX__ Don't index
__STATICREDIRECT__ Don't update redirect
```
### Page Variables
```wikitext
{{PAGENAME}} Page title without namespace
{{FULLPAGENAME}} Full page title
{{BASEPAGENAME}} Parent page name
{{SUBPAGENAME}} Subpage name
{{ROOTPAGENAME}} Root page name
{{TALKPAGENAME}} Associated talk page
{{NAMESPACE}} Current namespace
{{NAMESPACENUMBER}} Namespace number
{{PAGEID}} Page ID
{{REVISIONID}} Revision ID
```
### Site Variables
```wikitext
{{SITENAME}} Wiki name
{{SERVER}} Server URL
{{SERVERNAME}} Server hostname
{{SCRIPTPATH}} Script path
```
### Date/Time Variables
```wikitext
{{CURRENTYEAR}} 4-digit year
{{CURRENTMONTH}} Month (01-12)
{{CURRENTMONTHNAME}} Month name
{{CURRENTDAY}} Day (1-31)
{{CURRENTDAYNAME}} Day name
{{CURRENTTIME}} HH:MM
{{CURRENTTIMESTAMP}} YYYYMMDDHHmmss
```
### Statistics
```wikitext
{{NUMBEROFPAGES}} Total pages
{{NUMBEROFARTICLES}} Content pages
{{NUMBEROFFILES}} Files
{{NUMBEROFUSERS}} Registered users
{{NUMBEROFACTIVEUSERS}} Active users
{{NUMBEROFEDITS}} Total edits
{{PAGESINCATEGORY:Name}} Pages in category
```
## Template Examples
### Simple Infobox
```wikitext
<noinclude>{{Documentation}}</noinclude><includeonly>
{| class="infobox" style="width:22em"
|-
! colspan="2" style="background:#ccc" | {{{title|{{PAGENAME}}}}}
{{#if:{{{image|}}}|
{{!}}-
{{!}} colspan="2" {{!}} [[File:{{{image}}}|200px|center]]
}}
|-
| '''Type''' || {{{type|Unknown}}}
|-
| '''Date''' || {{{date|—}}}
|}
</includeonly>
```
### Navbox Template
```wikitext
<noinclude>{{Documentation}}</noinclude><includeonly>
{| class="navbox" style="width:100%"
|-
! style="background:#ccf" | {{{title|Navigation}}}
|-
| {{{content|}}}
|}
</includeonly>
```
### Citation Template
```wikitext
<includeonly>{{#if:{{{author|}}}|{{{author}}}. }}{{#if:{{{title|}}}|''{{{title}}}''. }}{{#if:{{{publisher|}}}|{{{publisher}}}{{#if:{{{year|}}}|, }}}}{{{year|}}}.{{#if:{{{url|}}}| [{{{url}}} Link]}}</includeonly>
```
## Tips
1. **Pipe trick**: `[[Help:Contents|]]` displays as "Contents"
2. **Escape pipes in templates**: Use `{{!}}` for literal `|`
3. **Trim whitespace**: Parameters automatically trim whitespace
4. **Check emptiness correctly**: `{{{param|}}}` vs `{{{param}}}` - the former has empty default, latter is undefined if not passed
5. **Subst for speed**: Use `{{subst:Template}}` for templates that don't need dynamic updates

View File

@ -0,0 +1,139 @@
---
name: wiki-sync-translate
description: 同步英文 MediaWiki 页面变更到中文翻译文档。当用户需要更新中文 Wiki页面、同步英文变更、或翻译 Wiki 内容时触发。适用于 Project Diablo 2 Wiki 的中英双语同步维护场景。
---
# Wiki 同步翻译
同步英文 Wiki 页面变更到中文翻译文档,保持行号一致。
## 使用方法
- 用户请求更新中文 Wiki 页面
- 用户请求同步英文 Wiki 变更
- 用户指定某个页面需要进行翻译更新
- 用户执行 `/wiki-sync-translate <页面名称>`
## 工作目录
脚本位于:`.claude/skills/wiki-sync-translate/scripts/wiki_sync.py`
## 执行步骤
### Step 1: 运行同步脚本
使用 skill 目录下的专用脚本获取变更:
```bash
cd /mnt/d/code/sync-pd2-wiki
source venv/bin/activate
python .claude/skills/wiki-sync-translate/
scripts/wiki_sync.py --title "<页面名称>" --since <上次同步时间> --run
```
参数说明:
- `--title`: 指定要同步的页面名称
- `--since`: 起始时间,格式如 `2026-01-02T12:07:05Z`
- `--run`: 必须提供此参数才会执行
### Step 2: 读取输出文件
脚本会在 `wiki_sync_output/<时间戳>/changed_pages/` 目录下生成:
| 文件 | 说明 | 用途 |
|------|------|------|
| `*.comparison.json` | 结构化变更信息 | **AI 读取,包含行号和变更内容** |
| `*.full.txt` | 英文最新版本 | 参考对照 |
| `*.cn.txt` | 中文原文 | **基于此文件进行修改** |
| `*.old.txt` | 英文历史版本 | 参考对照 |
### Step 3: 解析 comparison.json
`comparison.json` 格式:
```json
{
"title": "页面标题",
"has_cn_translation": true,
"summary": {
"total_changes": 1,
"replaced": 1,
"added": 0,
"removed": 0
},
"changes": [
{
"type": "replaced",
"old_line": 66,
"new_line": 66,
"old_content": "旧内容",
"new_content": "新内容"
}
]
}
```
变更类型:
- `replaced`: 替换,`old_line` 表示需要修改的行号
- `added`: 新增,`new_line` 表示插入位置
- `removed`: 删除,`old_line` 表示要删除的行
### Step 4: 更新中文文档
**核心原则:行号必须完全一致**
1. 创建 `wiki_sync_output/<时间戳>/result_pages/` 目录(如不存在)
2. 复制 `*.cn.txt` 内容到 `result_pages/<页面名>.cn.txt`
3. 根据 `changes` 中的行号定位对应行
4. 智能更新:
- 仅同步变更的内容
- 保留中文翻译(如 "赛季 12" 不改为 "Season 12"
- 新增的英文内容智能翻译成中文,可以用`grep` 命令在`references/PatchString.txt` 搜索技能或物品的中文名称,搜索不到的则智能翻译按照暗黑破坏神2的常用命名。
- 保持 MediaWiki 语法正确,语法可以参照`mediawiki-wikitest` skill.
5. 保存到 `wiki_sync_output/<时间戳>/result_pages/<页面名>.cn.txt`
### Step 5: 输出结果
将更新后的文档保存到 `wiki_sync_output/<时间戳>/result_pages/<页面名>.cn.txt`,用户可直接复制到 Wiki。
## 示例
**输入 - comparison.json:**
```json
{
"changes": [{
"type": "replaced",
"old_line": 66,
"old_content": "| style=\"...\"| 2025-11-25<br>(Season 12)",
"new_content": "| style=\"...\"| 2026-01-25<br>(Season 12)"
}]
}
```
**中文原文第66行**
```
| style="color:#3f6e2d; background-color:#161f0c; border-color:#0d1709"| 2025-11-25<br>(赛季 12)
```
**更新后第66行**
```
| style="color:#3f6e2d; background-color:#161f0c; border-color:#0d1709"| 2026-01-25<br>(赛季 12)
```
**变更说明:** 日期 `2025-11-25``2026-01-25`,但 `赛季 12` 保持中文不变。
## 注意事项
1. **行号一致性**:确保中英文文档行号完全对应,这是长期维护的基础
2. **保留翻译**:只同步变更内容,不替换已有的中文翻译
3. **MediaWiki 语法**
- 表格分隔符 `|-` 位置保持一致
- 链接格式 `[[页面名|显示文本]]` 不变
- 样式属性 `style="..."` 不变
4. **特殊字符**:注意 `<br>`、`&nbsp;` 等 HTML 实体
## 错误处理
- 如果 `has_cn_translation` 为 false提示用户该页面无中文翻译
- 如果 `is_new_page` 为 true说明是新页面需要全新翻译
- 如果找不到对应行,可能是中英文版本不同步,需要人工确认

View File

@ -0,0 +1,417 @@
# -*- coding: utf-8 -*-
"""
MediaWiki Wiki 同步工具 - AI Agent 版本
输出 JSON 格式的对比文件便于 AI Agent 读取和处理
"""
import os
import argparse
from pathlib import Path
from datetime import datetime, timedelta
import requests
from dotenv import load_dotenv
import difflib
import json
import re
# ==================== 配置区 ====================
load_dotenv()
WIKI_API_URL_EN = os.getenv("WIKI_API_URL_EN", "https://wiki.projectdiablo2.com/w/api.php")
WIKI_API_URL_CN = os.getenv("WIKI_API_URL_CN", "https://wiki.projectdiablo2.cn/w/api.php")
OUTPUT_DIR = Path("wiki_sync_output")
OUTPUT_DIR.mkdir(exist_ok=True)
CURRENT_OUTPUT_DIR = None
LAST_TIMESTAMP_FILE = "last_sync_timestamp.txt"
SESSION_EN = requests.Session()
SESSION_EN.headers.update({
"User-Agent": "WikiSyncTool/5.0 (AI Agent Version)"
})
SESSION_CN = requests.Session()
SESSION_CN.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})
SESSION_CN.trust_env = False
# ================================================
def load_last_timestamp():
if os.path.exists(LAST_TIMESTAMP_FILE):
with open(LAST_TIMESTAMP_FILE, encoding="utf-8") as f:
return f.read().strip()
return None
def save_last_timestamp(ts):
with open(LAST_TIMESTAMP_FILE, "w", encoding="utf-8") as f:
f.write(ts)
def get_recent_changes(since):
"""获取自 since 时间后每个页面的最新 revid"""
params = {
"action": "query",
"list": "recentchanges",
"rcprop": "title|ids|timestamp",
"rctype": "edit|new",
"rcdir": "newer",
"rcstart": since,
"rclimit": 500,
"format": "json"
}
latest = {}
while True:
try:
r = SESSION_EN.get(WIKI_API_URL_EN, params=params)
r.raise_for_status()
data = r.json()
if "error" in data:
raise Exception(data["error"])
for rc in data.get("query", {}).get("recentchanges", []):
latest[rc["title"]] = (rc["revid"], rc["timestamp"])
if "continue" not in data:
break
params.update(data["continue"])
except Exception as e:
print(f"获取最近更改时出错: {e}")
break
return latest
def get_old_revid(title, end_time):
"""获取指定时间前的最后一个 revid"""
params = {
"action": "query",
"prop": "revisions",
"titles": title,
"rvprop": "ids|timestamp",
"rvlimit": 1,
"rvdir": "older",
"rvstart": end_time,
"format": "json"
}
try:
r = SESSION_EN.get(WIKI_API_URL_EN, params=params).json()
pages = r["query"]["pages"]
page = next(iter(pages.values()))
if "revisions" in page:
return page["revisions"][0]["revid"]
except Exception as e:
print(f"获取旧版本ID时出错: {e}")
return None
def get_page_content(wiki_url, session, title, revid=None):
"""获取页面完整内容"""
params = {
"action": "query",
"prop": "revisions",
"titles": title,
"rvprop": "content|timestamp|ids",
"rvslots": "main",
"format": "json"
}
if revid:
params["rvstartid"] = revid
params["rvendid"] = revid
try:
r = session.get(wiki_url, params=params).json()
pages = r["query"]["pages"]
page = next(iter(pages.values()))
if "revisions" in page:
rev = page["revisions"][0]
return rev["slots"]["main"]["*"], rev["timestamp"], rev["revid"]
except Exception as e:
print(f"获取页面内容时出错: {e}")
return None, None, None
def generate_text_diff(old_text, new_text):
"""生成 unified diff 格式"""
if not old_text:
return "新创建页面"
old_lines = old_text.splitlines(keepends=True)
new_lines = new_text.splitlines(keepends=True)
return ''.join(difflib.unified_diff(old_lines, new_lines, lineterm='\n'))
def parse_diff_to_changes(diff_text):
"""
解析 diff 文本提取结构化的变更信息
返回一个列表每个元素包含变更类型行号旧内容新内容
"""
if not diff_text or diff_text.startswith("新创建页面"):
return []
changes = []
current_old_line = 0
current_new_line = 0
in_hunk = False
for line in diff_text.splitlines():
if line.startswith('@@'):
match = re.match(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@', line)
if match:
current_old_line = int(match.group(1))
current_new_line = int(match.group(3))
in_hunk = True
elif line.startswith('---') or line.startswith('+++'):
continue
elif in_hunk:
if line.startswith('-'):
changes.append({
"type": "removed",
"old_line": current_old_line,
"new_line": None,
"old_content": line[1:],
"new_content": None
})
current_old_line += 1
elif line.startswith('+'):
changes.append({
"type": "added",
"old_line": None,
"new_line": current_new_line,
"old_content": None,
"new_content": line[1:]
})
current_new_line += 1
elif line.startswith(' '):
current_old_line += 1
current_new_line += 1
return changes
def group_changes_by_line(changes):
"""
将变更按行号分组将连续的删除和添加合并为替换操作
"""
# 先收集所有的删除和添加
removed_by_line = {} # old_line -> content
added_by_line = {} # new_line -> content
for c in changes:
if c["type"] == "removed":
removed_by_line[c["old_line"]] = c["old_content"]
elif c["type"] == "added":
added_by_line[c["new_line"]] = c["new_content"]
# 尝试将删除和添加配对
grouped = []
used_added = set()
for old_line, old_content in sorted(removed_by_line.items()):
# 找一个未使用的添加行来配对
paired = False
for new_line, new_content in sorted(added_by_line.items()):
if new_line not in used_added:
grouped.append({
"type": "replaced",
"old_line": old_line,
"new_line": new_line,
"old_content": old_content,
"new_content": new_content
})
used_added.add(new_line)
paired = True
break
if not paired:
grouped.append({
"type": "removed",
"old_line": old_line,
"new_line": None,
"old_content": old_content,
"new_content": None
})
# 添加未配对的新增行
for new_line, new_content in sorted(added_by_line.items()):
if new_line not in used_added:
grouped.append({
"type": "added",
"old_line": None,
"new_line": new_line,
"old_content": None,
"new_content": new_content
})
# 按行号排序
grouped.sort(key=lambda x: x["old_line"] or x["new_line"] or 0)
return grouped
def create_diff_json(title, en_old_content, en_new_content, cn_content):
"""
创建结构化的 JSON 对比数据仅包含英文变更AI自行匹配中文
"""
# 生成英文 diff
diff_text = generate_text_diff(en_old_content, en_new_content)
# 解析变更
raw_changes = parse_diff_to_changes(diff_text)
grouped_changes = group_changes_by_line(raw_changes)
# 构建输出结构(精简版,不含中文行内容)
result = {
"title": title,
"timestamp": datetime.now().isoformat(),
"is_new_page": diff_text == "新创建页面",
"has_cn_translation": cn_content is not None,
"summary": {
"total_changes": len(grouped_changes),
"replaced": len([c for c in grouped_changes if c["type"] == "replaced"]),
"added": len([c for c in grouped_changes if c["type"] == "added"]),
"removed": len([c for c in grouped_changes if c["type"] == "removed"])
},
"changes": grouped_changes
}
return result
def save_files(title, diff_json, en_full_text, cn_content, timestamp, revid=None, old_full_text=None):
"""保存文件"""
global CURRENT_OUTPUT_DIR
if CURRENT_OUTPUT_DIR is None:
current_time_str = datetime.now().strftime("%Y%m%d_%H%M%S")
CURRENT_OUTPUT_DIR = OUTPUT_DIR / current_time_str
CURRENT_OUTPUT_DIR.mkdir(exist_ok=True)
(CURRENT_OUTPUT_DIR / "new_pages").mkdir(exist_ok=True)
(CURRENT_OUTPUT_DIR / "changed_pages").mkdir(exist_ok=True)
print(f"创建输出目录: {CURRENT_OUTPUT_DIR}")
safe_title = "".join(c if c.isalnum() or c in " -_." else "_" for c in title)
time_str = timestamp[:19].replace("-", "").replace(":", "").replace("T", "_")
base_filename = f"{safe_title}-{time_str}-{revid}" if revid else f"{safe_title}-{time_str}"
is_new_page = diff_json["is_new_page"]
if is_new_page:
target_dir = CURRENT_OUTPUT_DIR / "new_pages"
print(f" 检测到新页面")
# 保存英文完整内容
full_file = target_dir / f"{base_filename}.full.txt"
with open(full_file, "w", encoding="utf-8") as f:
f.write(en_full_text)
print(f" → 已保存: {full_file.relative_to(OUTPUT_DIR)}")
else:
target_dir = CURRENT_OUTPUT_DIR / "changed_pages"
# 保存英文完整内容
full_file = target_dir / f"{base_filename}.full.txt"
with open(full_file, "w", encoding="utf-8") as f:
f.write(en_full_text)
print(f" → 已保存: {full_file.relative_to(OUTPUT_DIR)}")
# 保存中文内容
if cn_content:
cn_file = target_dir / f"{base_filename}.cn.txt"
with open(cn_file, "w", encoding="utf-8") as f:
f.write(cn_content)
print(f" → 已保存: {cn_file.relative_to(OUTPUT_DIR)}")
# 保存 JSON 对比文件(核心输出)
json_file = target_dir / f"{base_filename}.comparison.json"
with open(json_file, "w", encoding="utf-8") as f:
json.dump(diff_json, f, ensure_ascii=False, indent=2)
print(f" → 已保存: {json_file.relative_to(OUTPUT_DIR)} (AI Agent 对比文件)")
# 保存历史版本
if old_full_text:
old_file = target_dir / f"{base_filename}.old.txt"
with open(old_file, "w", encoding="utf-8") as f:
f.write(old_full_text)
print(f" → 已保存: {old_file.relative_to(OUTPUT_DIR)}")
def process_single_page(title, since_time, update_timestamp=False):
"""处理单个页面"""
print(f"正在处理页面:{title}")
# 获取最新内容
latest_content, latest_ts, latest_revid = get_page_content(WIKI_API_URL_EN, SESSION_EN, title)
if latest_content is None:
print("页面不存在或被删除")
return None
# 获取旧版本
old_revid = get_old_revid(title, since_time)
old_content = None
if old_revid:
old_content, _, _ = get_page_content(WIKI_API_URL_EN, SESSION_EN, title, old_revid)
if old_content is None:
print(" 无法获取历史版本,视为新页面")
# 获取中文翻译
print(" 搜索中文翻译...")
cn_content = None
# 直接尝试获取同名页面
cn_result, _, _ = get_page_content(WIKI_API_URL_CN, SESSION_CN, title)
if cn_result:
cn_content = cn_result
print(f" 找到中文页面 ({len(cn_content)} 字符)")
else:
print(" 未找到中文翻译")
# 生成对比 JSON
diff_json = create_diff_json(title, old_content, latest_content, cn_content)
print(f" 变更统计: 替换={diff_json['summary']['replaced']}, "
f"新增={diff_json['summary']['added']}, 删除={diff_json['summary']['removed']}")
# 保存文件
save_files(title, diff_json, latest_content, cn_content, latest_ts, latest_revid, old_content)
if update_timestamp:
save_last_timestamp(latest_ts)
print(f"已更新时间戳 → {latest_ts}")
return latest_ts
def process_all_pages_since(since_time):
"""处理所有变更页面"""
print("正在获取最近变更列表...")
changes = get_recent_changes(since_time)
if not changes:
print("没有发现任何变更")
return
latest_global_ts = since_time
for title, (revid, ts) in changes.items():
print(f"\n处理:{title}")
page_ts = process_single_page(title, since_time)
if page_ts and page_ts > latest_global_ts:
latest_global_ts = page_ts
save_last_timestamp(latest_global_ts)
print(f"\n同步完成!最新时间戳: {latest_global_ts}")
print(f"文件保存在: {CURRENT_OUTPUT_DIR.resolve() if CURRENT_OUTPUT_DIR else OUTPUT_DIR.resolve()}")
def main():
parser = argparse.ArgumentParser(description="MediaWiki 同步工具 - AI Agent 版本")
parser.add_argument("--since", type=str, help="起始时间,格式: 2025-11-28T00:00:00Z")
parser.add_argument("--title", type=str, help="只处理指定页面")
parser.add_argument("--update-timestamp", action="store_true", help="更新全局时间戳")
parser.add_argument("--run", action="store_true", help="执行同步")
args = parser.parse_args()
if not args.run:
parser.print_help()
return
since_time = args.since or load_last_timestamp()
if not since_time:
since_time = (datetime.utcnow() - timedelta(days=1)).isoformat(timespec='seconds') + "Z"
print(f"起始时间: {since_time}")
if args.title:
process_single_page(args.title.strip(), since_time, args.update_timestamp)
else:
process_all_pages_since(since_time)
if __name__ == "__main__":
main()

32
sync.py
View File

@ -41,8 +41,9 @@ SESSION_EN.headers.update({
SESSION_CN = requests.Session()
SESSION_CN.headers.update({
"User-Agent": "WikiSyncTool/4.0 (your-email@example.com; MediaWiki Sync Bot)"
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
})
SESSION_CN.trust_env = False # 禁用环境变量代理
# ================================================
def load_last_timestamp():
@ -264,31 +265,24 @@ def parse_diff_with_line_numbers(diff_text):
def search_chinese_page(title):
"""在中文wiki中搜索对应的页面"""
# 首先尝试精确匹配
# 首先尝试直接获取页面因为中文wiki禁用了标题搜索
params = {
"action": "query",
"list": "search",
"srsearch": f'"{title}"',
"srwhat": "title",
"srlimit": 5,
"prop": "revisions",
"titles": title,
"rvprop": "ids",
"format": "json"
}
try:
r = SESSION_CN.get(WIKI_API_URL_CN, params=params).json()
search_results = r.get("query", {}).get("search", [])
r = SESSION_CN.get(WIKI_API_URL_CN, params=params, timeout=10)
data = r.json()
pages = data.get("query", {}).get("pages", {})
if search_results:
# 返回第一个匹配的结果
return search_results[0]["title"]
# 如果精确匹配没有结果,尝试模糊搜索
params["srsearch"] = title.replace(" ", "%20")
r = SESSION_CN.get(WIKI_API_URL_CN, params=params).json()
search_results = r.get("query", {}).get("search", [])
if search_results:
return search_results[0]["title"]
for page_id, page_info in pages.items():
# page_id 为负数表示页面不存在
if page_id != "-1" and "missing" not in page_info:
return page_info.get("title")
except Exception as e:
print(f"搜索中文页面时出错: {e}")