Communitygithub.com

DrunkMan9o/agent-skills

Localisez les interfaces utilisateur des jeux Unity (Mono) ou de toute application .NET en chinois simplifié, avec un moteur d'auto-vérification intégré qui garantit l'intégrité, la cohérence, le format et l'absence de risques structurels avant l'écriture. Idéal pour les projets où le texte est codé en dur dans le tas de chaînes #US de l'Assembly-CSharp.dll, sans table de localisation, et avec une police dynamique intégrée.

Qu'est-ce que agent-skills ?

Cette skill implémente un pipeline complet de localisation en chinois pour les jeux Unity (Mono) et les applications .NET dont le texte est codé en dur dans les assemblys managés. Elle se compose de quatre phases : extraction des chaînes via ldstr, analyse des risques (indiquant quelles chaînes traduire ou ne pas traduire), application des traductions, et auto-vérification post-écriture. L'auto-vérification, point central de l'outil, inspecte 31 points de contrôle répartis en six familles (complétude, cohérence, format, risque, structure, exécution) et peut bloquer la sortie si des seuils de sévérité (P0, P1, P2) ne sont pas respectés. La skill s'appuie sur des fichiers de configuration (l10n.json), des tables de traduction (globale, par site, pour les énumérations) et un dictionnaire de terminologie. Elle est conçue pour fonctionner avec les polices dynamiques intégrées et gère la substitution de police par sous-ensembles de caractères. L'outil est invoqué via un script Python unique (zhl10n.py) et nécessite les dépendances dnfile, dncil, UnityPy et fonttools.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/DrunkMan9o/agent-skills/tree/HEAD/skills/unity-zh-l10n

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Pipeline de localisation en chinois pour Unity (avec auto-vérification)

Transforme le processus « extraction → analyse des risques → traduction → écriture → auto-vérification » en un pipeline réutilisable en quatre phases. Le cœur n'est pas la traduction elle-même, mais déterminer d'abord quelles chaînes ne doivent pas être traduites — c'est le seul endroit où l'on peut casser le jeu.

Quand l'utiliser

Avant de commencer, vérifiez ces trois conditions ; si l'une d'elles n'est pas remplie, ne poursuivez pas :

  1. Où se trouve le texte : Jeux Unity Mono/IL2CPP dont le texte est codé en dur dans les assemblys managés (*_Data/Managed/Assembly-CSharp.dll) ou dans resources.assets, sans table de localisation, fichier .po, .csv ou autre ressource de localisation. Si une table de localisation existe, utilisez-la directement, ne modifiez pas l'assembly.
  2. Vous pouvez obtenir le fichier original : Comme vous modifiez un fichier exécutable, vous devez absolument disposer d'une copie originale non modifiée comme référence (répertoire de sauvegarde, fichier retéléchargé après vérification Steam, etc.). Sans référence, ne modifiez pas le fichier sur place.
  3. Le chinois peut s'afficher : Le projet utilise une police dynamique intégrée (TMP ou Legacy Font). Les polices statiques / atlas nécessitent un changement de texture ; ce pipeline ne gère que les polices dynamiques.

L'IL2CPP (pas de Assembly-CSharp.dll, seulement GameAssembly.dll) n'est pas pris en charge ; il nécessite Il2CppDumper + recompilation, ce qui est une autre histoire.

Démarrage rapide

# 0) Créer la structure du projet (génère l10n.json)
python <skill>/scripts/zhl10n.py init -C votre_répertoire_projet

# 1) Lister tous les sites ldstr (environ 10 s pour 5000 entrées)
python <skill>/scripts/zhl10n.py extract

# 2) Voir la classification des risques : ce qui peut être traduit, ce qui ne peut l'être que de manière ciblée, ce qui ne doit absolument pas l'être
python <skill>/scripts/zhl10n.py risk

# 3) Remplir trans_map.json / site_map.json, puis appliquer (ne modifie pas l'original, sortie dans out/build/)
python <skill>/scripts/zhl10n.py apply

# 4) Auto-vérification (lit par défaut les artefacts dans out/build)
python <skill>/scripts/zhl10n.py audit --fail-on P1

Tout-en-un : zhl10n.py all (extract → apply → audit).

Installer les dépendances : pip install dnfile dncil UnityPy fonttools. Pour exécuter uniquement audit, pas besoin de UnityPy.

Responsabilités de chaque étape

ÉtapePoint d'entréeEntréeSortiePérimètre
initzhl10n.py initChemin du jeu ciblel10n.json, tables videsGénère uniquement la configuration, ne touche pas aux fichiers du jeu
extractextract.pyDLL originale (+ assets optionnels)out/extract/sites.jsonChaque entrée de sites.json contient str/type/method/token/offsetLecture seule. Doit lire l'original non patché, sinon des chaînes déjà traduites seront extraites
riskrisk.pysites.jsonStatistiques de classification dans la consoleUniquement analyse, ne modifie aucune table
applyapply.pyDLL originale + tables de traduction + sous-ensemble de policeout/build/Assembly-CSharp.dllout/build/resources.assetsÉcrit de nouveaux fichiers, l'original reste intact (--inplace modifie sur place, conserve d'abord un .orig). Toute traduction bloquée est refusée à l'écriture et génère une erreur
auditaudit.pyArtefacts + tables de traduction + glossaire + logsout/audit/audit.{json,md,html}fixlist.csvLecture seule. Peut être exécuté sur n'importe quelle version de fichier, utile pour les comparaisons avant/après

Trois tables de traduction, chacune avec un rôle différent :

  • trans_map.json — Table globale, remplace la chaîne partout où elle apparaît. Ne mettez que les chaînes dont la traduction en chinois ne cassera pas la logique.
  • site_map.json — Table ciblée, la clé est type::méthode, ne modifie le ldstr qu'à cet endroit. Utile lorsque « le même mot doit être traduit dans le menu mais pas dans le système de saisie ».
  • enum_map.json — Table des noms d'énumération, modifie les noms de champs dans le tas #Strings. Pour les noms d'affichage des énumérations obtenus par ToString().

Ordre de décision : Ciblée → Globale. Si la ciblée correspond, la globale n'est pas utilisée.

Auto-vérification

L'auto-vérification est le point central de ce pipeline. Elle lit les artefacts après l'écriture, indépendamment des tables de traduction.

Sorties

FichierContenu
audit.jsonRésultat structuré complet : chaque constat contient check/severity/count/items[]/hint
audit.mdRésumé lisible par un humain, trié par P0→P2
audit.htmlRapport pliable dans un seul fichier
fixlist.csvListe des éléments à corriger, une ligne par élément, prête à être utilisée comme liste de tâches

Points de contrôle (31 points, 6 familles)

CV Complétude — Y a-t-il des omissions ?

N°NiveauVérificationCritère
CV1P1Texte non traduitLe site est affichable + aucune traduction dans les deux tables + non exempté par les règles de risque
CV1bP2Texte ressemblant à une interface mais bloquéIndique une contrainte du moteur, pas une omission de traduction ; nécessite une traduction ciblée et un test réel
CV2P1Traduction vide / placeholderLa traduction est vide, contient TODO/à traduire
CV3P1Traduction d'une phrase longue identique à l'originalProbablement pas traduite du tout
CV4P2Traduction d'un mot court/symbole identique à l'originalConserver l'original pour les icônes, noms de touches est généralement correct
CV5P2Entrée morte dans la table cibléeDéclarée mais introuvable dans l'IL original ; le nom de méthode a changé ou elle est couverte par la table globale

CS Cohérence — La terminologie est-elle correcte ?

N°NiveauVérification
CS1P2Même texte original traduit différemment dans la table globale et la table ciblée
CS2P1Traduction obligatoire du glossaire non respectée
CS3P1La traduction correspond à un mot interdit du glossaire
CS4P2Clés en double (différentes uniquement par la casse/les espaces)
CS5P2Style de ponctuation chinois tendant vers les demi-largeurs

FM Format — La concaténation casse-t-elle ?

N°NiveauVérificationConséquence
FM1P1Non-conservation des placeholdersString.Format lève une exception ou des nombres disparaissent
FM1bP2Suppression des placeholders de pluriel anglaisBonne pratique, juste une confirmation que l'on n'a pas supprimé un vrai paramètre
FM2P2Nombre de sauts de ligne différentProblème de mise en page
FM3P1Perte des espaces de début/fin dans les unités/fragments concaténés1000coins collé
FM4P2Autre changement d'espacement de début/finGénéralement sans conséquence entre caractères chinois
FM5/FM6P2Ponctuation demi-largeur / espaces consécutifsApparence
FM7P2Expansion excessive de la longueur de la traductionSouvent le résultat d'avoir mis une description dans une étiquette

RK Risque — Le jeu peut-il être cassé ?

N°NiveauVérification
RK1P0Chaîne à haut risque incluse dans la table de traduction (chemin, clé moteur, expression régulière, nom d'action de saisie…)
RK2P2La table globale contient un identifiant unique, recommander un passage en ciblé
RK3P1La clé de la table de traduction contient du chinois, deuxième application confuse
RK4P2La traduction contient des fragments anglais résiduels

IG Structure — Le fichier lui-même est-il toujours valide ?

N°NiveauVérification
IG0/IG1/IG2/IG4P0Fichier cible manquant, anomalie PE/métadonnées, #US impossible à analyser, jeton ldstr invalide, nombre de lignes de métadonnées différent de l'original
IG3P2Site toujours en anglais après le patch (liste blanche attendue, pour vérification)
IG5/IG6P1Caractères de police manquants / fichier de police impossible à analyser

RT Exécution — Le jeu a-t-il signalé quelque chose ?

N°NiveauVérification
RT1P1Trace d'exception dans Player.log (nécessite la configuration source.logs)

Modes de déclenchement

Trois modes, selon le scénario :

# 1. Auto-vérification immédiate après écriture (le plus courant en boucle de développement)
zhl10n.py audit

# 2. Comme passerelle : en CI ou avant publication, sortie non nulle si le seuil n'est pas atteint
zhl10n.py audit --fail-on P0      # Échoue dès qu'un blocage est présent
zhl10n.py audit --fail-on P1      # Sévérité par défaut, recommandé pour les publications
zhl10n.py audit --fail-on P2      # Même les avertissements doivent être résolus

# 3. Comparaison de régression : quels problèmes supplémentaires par rapport à la dernière fois
zhl10n.py audit --baseline

La phase apply exécute également automatiquement une audit à la fin, et affiche les conclusions — l'étape d'écriture ne produit pas de paquet défectueux sans que l'on le sache.

Comment utiliser les résultats de l'audit

  1. Vérifiez d'abord les P0. Aucun P0 ne doit être publié, cela signifie « le jeu pourrait ne pas démarrer ».
  2. Vérifiez ensuite les P1. CV1 est la liste des omissions de traduction, FM1 est la liste des crashes certains, RT1 est la preuve que le jeu a déjà crashé.
  3. Les P2 servent de liste de vérification : CV1b vous dit quels points sont des contraintes du moteur et non des omissions, CV5/CS4 sont des entrées poubelles à supprimer directement.
  4. fixlist.csv se traite ligne par ligne ; corrigez, relancez, jusqu'à P0=0 et P1=0.

Configuration (l10n.json)

{
  "project": "NomDuJeu", "version": "1.0",
  "source": {
    "dll":     "…/Managed/Assembly-CSharp.dll",   // Uniquement pour localiser, extract lit backup
    "backup":  "…/_Backup/Assembly-CSharp.dll",   // Doit être l'original non modifié
    "assets":  ["…/resources.assets"],
    "logs":    ["…/Player.log"]                    // Optionnel, pour RT1
  },
  "output": { "dir": "out" },
  "text":   { "map": "trans_map.json", "site_map": "site_map.json", "enum_map": "enum_map.json" },
  "glossary": "glossary.csv",
  "font":   { "subset": "font/zh-subset.ttf", "source": "font/NotoSansSC-Bold.otf", "targets": {} },
  "audit":  { "severity_fail": "P1", "max_expansion_ratio": 2.5, "max_expansion_abs": 40 },
  "risk":   { "extra_patterns": [], "extra_allow": [] }
}

Deux issues de secours, pensez-y en cas d'erreur :

  • risk.extra_patterns — Chaînes connues pour être intraduisibles dans le projet (expressions régulières). Exemple : ^Levels$
  • risk.extra_allow — Chaînes spécifiques déjà confirmées comme n'ayant pas besoin d'être traduites. Les ajouter ici ne les considère pas comme des omissions

Glossaire glossary.csv : trois colonnes en,zh,forbidden, forbidden contient des synonymes séparés par |. CS2/CS3 en dépendent ; s'il est absent, ces deux vérifications sont ignorées.

Règles de décision : quelles chaînes ne doivent absolument pas être traduites

Version courte : Les chaînes utilisées comme « clés » dans le code, si on les traduit, on ne trouve plus rien.

CatégorieExempleQue se passe-t-il si on les traduit ?
Chemin de ressource"Levels", "UI/Fonts/X"Table des niveaux vide → NRE lors du chargement de la sauvegarde → blocage sur l'écran de chargement
Action de saisie / nom de champMove/Up/ButtonLes liaisons de touches ne fonctionnent plus
Champ de sauvegardelegacySkillPointCreditLa sauvegarde ne se charge pas
Nom d'énumérationDeposit (utilisé ailleurs que dans ToString(), comme Enum.Parse)L'analyse échoue
Motif d'expression régulière([0-9.]+)% more coinsLa réécriture du texte d'indice échoue, retour à l'anglais
Nom d'objet / de ressourcenew GameObject("Display glass")Généralement ne casse pas, mais pollue les noms de ressources
Chaîne de format{0:N0} / {1:N0}String.Format lève une exception

Même forme, destin différent : Pause dans le menu des paramètres est un texte de bouton (à traduire), dans GameInput c'est un nom d'action (à ne pas traduire). Il est impossible de les distinguer par la forme du texte ; seul le fait de savoir « dans quel type, dans quelle méthode » ça se trouve permet de trancher. C'est ce que fait _code_shape() dans risk.py :

  • Les littéraux dans les méthodes de construction (.ctor/Build/Initialize/Create/Ensure…) → noms, ne pas traduire
  • Les littéraux dans les méthodes d'affichage (Draw/OnGUI/Format/get_…) → textes, traduire
  • Les littéraux dans les méthodes de comparaison (Is/Has/Matches/Find…) → clés de correspondance, ne pas traduire

Après avoir ajouté une nouvelle règle, vous devez la valider avec un test négatif : insérez délibérément "Levels": "Niveaux" dans la table de traduction, et vérifiez que l'audit remonte un P0.

Les pièges des polices

Les polices dynamiques intégrées ne peuvent être remplacées que par une substitution de même taille. Réécrire entièrement resources.assets modifiera les décalages m_StreamData des Texture2D, et toutes les textures seront décalées.

Processus : l'étape font crée un sous-ensemble basé sur « l'ensemble des caractères chinois réellement utilisés », le convertit en TTF (Unity est instable avec les CFF/OTF), puis le replace dans m_FontData. Si le nombre de glyphes est trop important, réduisez la taille de la police ou le poids, mais ne dépassez pas la taille originale.

Les polices comme Bangers-Regular, qui ne font que quelques dizaines de Ko, ne peuvent pas contenir le chinois — redirigez le chemin de la police vers une autre police chinoise (dans font.targets de l10n.json), c'est plus fiable que de forcer.

Liste de vérification avant livraison

[ ] audit --fail-on P1 passé
[ ] Lancement réel du jeu, parcourez chaque écran : menu principal / paramètres / liste des sauvegardes / HUD en jeu / fenêtre contextuelle / erreur
[ ] Aucune nouvelle exception dans Player.log (RT1 = 0)
[ ] L'emplacement de la sauvegarde originale est documenté dans les notes de version (c'est la seule façon de restaurer)
[ ] Enregistrez la somme MD5 de l'artefact dans les notes de version

Méthode de diagnostic en cas d'erreur : remplacez d'abord uniquement la DLL originale, conservez le reste ; si tout fonctionne, le problème vient du patch de l'assembly ; puis remplacez uniquement la DLL, conservez le reste ; si le problème se reproduit, c'est confirmé. La dichotomie est plus rapide que la lecture du code.

Documentation de référence

  • references/flow.md — Implémentation détaillée et structures de données de chaque phase
  • references/audit-spec.md — Définition complète des 31 points de contrôle, niveaux, instructions de correction
  • references/risk-rules.md — Table des règles de risque et les accidents réels derrière
  • assets/l10n.example.json / assets/glossary.example.csv — Modèles de configuration

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Skills associés