Compare commits

...

2 Commits

Author SHA1 Message Date
Philippe Tourigny 58d99804c2 Ajouter les fichiers texte 2023-06-01 00:07:36 +09:00
Philippe Tourigny 75b6eec000 Ajouter les variantes du script 2023-06-01 00:07:19 +09:00
6 changed files with 580 additions and 0 deletions

View File

@ -0,0 +1,5 @@
Steps:
1. Read the contents of two files.
2. Combine those contents.
3. Convert the result to the TMX format.
4. Write the converted data to a file.

View File

@ -0,0 +1,5 @@
Étapes:
1. Lire le contenu de deux fichiers;
2. Combiner ces contenus;
3. Convertir ce résultat en TMX;
4. Écrire le résultat de la conversion dans un fichier.

View File

@ -0,0 +1,5 @@
ステップ
1.二つのファイルの内容を読み込む。
2.その内容を結合させる。
結合の結果をTMXに変換。
4.変換の結果をファイルに書き込む。

View File

@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
from pathlib import Path
from lxml import etree as et
### Définir les constantes ###
# Espace de nom XML et attribut @xml:lang
XMLLANG = et.QName('http://www.w3.org/XML/1998/namespace', 'lang')
# Valeurs à saisir via une interface graphique dans la version finale.
CORPUSPATH = Path(Path().cwd()/'fichiers_texte')
SOURCE_LANG = 'fr'
### Fonctions ###
def get_languages_and_files(corpuspath):
'''Récupérer les fichiers et leur langues.'''
textfiles = corpuspath.glob('*.txt')
languages_and_files = {l.stem.rsplit('_')[1]:l for l in textfiles}
return languages_and_files
def read_content_by_language(languages_and_files):
'''Lire le contenu du fichier pour chacune des langues'''
# Créer une liste de language avec la langue source en tête.
languages = list(languages_and_files.keys())
languages.insert(0,languages.pop(languages.index(SOURCE_LANG)))
# Lire le contenu du fichier correspondant à chacune des langues.
contents = []
for language in languages:
with open(languages_and_files[language], 'r', encoding='utf8') as c:
contents.append(c.read().splitlines())
return (languages, contents)
def prepare_units(contents):
'''Jumeler les phrases correspondantes dans chacune des langues.'''
units = zip(*contents)
return units
def create_tmx_document():
'''Créer la structure de base de la TMX.'''
# Créer la racine.
tmx = et.Element('tmx', attrib={'version': '1.4'})
# Définir les attributs de l'élément <header>.
tmxheader = {'creationtool':'Utilitaire stage 2023',
'creationtoolversion': '0.3', 'datatype': 'plaintext',
'segtype': 'sentence', 'adminlang': 'en-US',
'srclang': SOURCE_LANG, 'o-tmf': 'text files'}
# Créer les éléments <header> et <body>.
header = et.SubElement(tmx, 'header', attrib=tmxheader)
body = et.SubElement(tmx, 'body')
return tmx
def make_tu(languages, unit):
'''Créer un élément <tu> contenant les <tuv> et <seg> pour toutes les langues d'un segment.'''
tu = et.Element('tu')
for l, language in enumerate(languages):
# Définir et ajouter l'élément <tuv>
tuv = et.SubElement(tu, 'tuv', {XMLLANG: language})
# Définir et ajouter l'élément <seg>
seg = et.SubElement(tuv, 'seg')
seg.text = unit[l]
return tu
def build_final_tree(tmx):
'''Créer l'arbre XML au complet pour la TMX.'''
# Créer l'arbre
tmxtree = et.ElementTree(tmx)
# Ajouter le DTD au niveau du système.
tmxtree.docinfo.system_url = 'tmx14.dtd'
return tmxtree
def save_tmx(basename, tmxtree):
'''Définir le chemin et le nom complet du fichier TMX, et le sauvegarder.'''
# Définir le chemin et nom du fichier.
tmxext = '.tmx'
tmxname = basename + '_lxml_etree' + tmxext
tmxpath = Path(CORPUSPATH.parent/'fichier_tmx')
# Créer le chemin s'il n'existe pas.
if not tmxpath.exists():
tmxpath.mkdir(parents=True, exist_ok=True)
tmxfile = Path(tmxpath/tmxname)
# Écrire le fichier
tmxtree.write(tmxfile, encoding='utf-8',
xml_declaration=True, pretty_print=True)
### Programme principal ###
if __name__ == '__main__':
# Récupérer les langues et le contenu des fichiers
languages_and_files = get_languages_and_files(CORPUSPATH)
languages, contents = read_content_by_language(languages_and_files)
# Définir le nom de base du fichier
basename = languages_and_files[SOURCE_LANG].stem.strip('_'+SOURCE_LANG)
# Créer un document TMX
tmx = create_tmx_document()
# Jumeler les unités de traduction
units = prepare_units(contents)
# Créer les éléments `<tu>` avec les `<tuv>` contenant le texte des
# segments pour chacune des langues.
for unit in units:
tu = make_tu(languages, unit)
tmx[1].append(tu)
# Compléter l'arbre XML
tmxtree = build_final_tree(tmx)
# Sauvegarder le fichier TMX
save_tmx(basename, tmxtree)

View File

@ -0,0 +1,272 @@
# -*- coding: utf-8 -*-
'''
Créer une mémoire de traduction à partir de fichiers texte parallèles.
Cet utilitaire produit un fichier TMX conforme à la version 1.4 de la spécification TMX (https://www.gala-global.org/tmx-14b, en anglais) à partir de fichiers encodé en UTF-8 qui contiennent le même nombre de lignes.
Limitations:
1. Les fichiers texte doivent être placé dans un répertoire `fichiers_texte`
du répertoire de l'utilitaire.
2. Ces fichiers doivent avoir une extension `.txt` et être encodés en UTF-8.
Leur nom doit se terminer par `_LL`, LL est le code de la langue du
fichier.
3. On suppose que la mémoire part d'un texte en français, qui est donc
désigné comme langue source. (Cela signifie également qu'il doit
impérativement y avoir un fichier `texte_fr.txt` parmi les fichiers
à aligner.)
4. Le fichier TMX est sauvegardé dans un répertoire `fichier_tmx` créé dans
le répertoire de l'utilitaire, avec le même nom de base que celui du
fichier en français.
Utilisation:
1. Mettez les fichiers à aligner dans le sous-répertoire `fichiers_texte`,
en vous assurant qu'ils sont conformes aux limitations ci-dessus.
2. Lancez le script depuis la ligne de commande ou votre éditeur.
Avec un éditeur, il est possible que le chemin d'exécution ne soit pas
celui du script, et vous obtiendrez une erreur parce que le script ne
trouvera pas les fichiers texte.
Dans un tel cas, vérifiez les réglages de votre éditeur, ou modifiez
le chemin des fichiers dans le code.
'''
##################################################
#
# textes_à_tmx.py
#
##################################################
# Version 0.3.1 du 06/01/2023
#
# Auteur: Philippe Tourigny (kazephil@gmail.com)
#
# Code créé à titre d'exemple dans le cadre du stage
# des M2 TRE du printemps 2023.
#
# À faire:
#
# L'utilitaire a été conçu en supposant la création d'une interface graphique
# dans une version ultérieure.
# L'interface graphique prévoit:
# - permettre de sélectionner les fichiers à aligner;
# - permettre de saisir les langues source et cible(s);
# - permettre de sélectionner le répertoire et le nom du fichier TMX
# - donner le choix de continuer ou d'arrêter si les fichiers ne contiennent
# pas tous le même nombre de lignes.
#
# Autres améliorations à apporter:
# - Ajouter du code pour traiter les erreurs.
#
# Licence
# Le code est distribué sous la license GPL3+
# https://www.gnu.org/licenses/gpl-3.0.en.html
#
##################################################
## Paramètres d'importation ##
# La classe `Path` est importée pour simplifier la manipulation des fichiers.
# La classe `etree` et `objectify` du module `lxml` sont importées pour
# assurer la validité du XML.
# La classe `objectify` du module `lxml` n'est pas essentielle, mais a été
# choisie parce qu'elle simplifie l'accès aux sous-éléments d'un élément XML.
from pathlib import Path
from lxml import etree as et
from lxml import objectify
## Constantes ##
# En Python, contrairement à d'autres langages de programmation, il n'y a pas
# de «vraies» constantes. Par convention, on désigne des variables toute en
# majuscules comme «constantes» dont on évite de changer la valeur ailleurs
# dans le code.
# Contante pour créer l'attribut «xml:lang», qui contient l'espace de
# nom «xml» et qui ne peut être entrée directement dans le code à cause
# du deux-points.
XMLLANG = et.QName('http://www.w3.org/XML/1998/namespace', 'lang')
# J'ai découver la méthode `.QName()` grâce à un stagiaire. À l'origine,
# le code utilisait deux constantes, une pour l'espace de nom, et l'autre
# pour créer l'attribut au complet:
# XML = 'http://www.w3.org/XML/1998/namespace' # Espace de nom «xml»
# LANG = '{' + XML + '}' + 'lang' # Attribut «xml:lang»
# Valeurs à récuperer via une interface graphique dans une version ultérieure
CORPUSPATH = Path(Path().cwd()/'fichiers_texte')
SOURCE_LANG = 'fr'
## Fonctions ##
# Les fonctions permettent de modulariser le code, ce qui le rend plus facile à modifier, déboguer, et simplifie aussi l'ajout de nouvelles fonctinnalités.
def get_languages_and_files(corpuspath):
'''Récupérer les fichiers et leur langues.'''
# La méthode `.glob()` produit une liste de fichiers correspondant
# au modèle précisé.
textfiles = corpuspath.glob('*.txt')
# On utilise ici une technique appelée "dictionary comprehension" pour
# récupérer le code de langue en tant que clé du dictionnaire, et l'objet
# Path correspondant au fichier pour la valeur.
languages_and_files = {l.stem.rsplit('_')[1]:l for l in textfiles}
return languages_and_files
def read_content_by_language(languages_and_files):
'''Lire le contenu du fichier pour chacune des langues'''
# Créer une liste de language avec la langue source en tête.
# On produit d'abord une liste qui ne contient que les langues.
languages = list(languages_and_files.keys())
# On s'assure que le français arrive en premier en le retirant
# de la liste (méthode .pop()) et en le réinsérant en tête de
# liste (méthode .insert()).
languages.insert(0,languages.pop(languages.index(SOURCE_LANG)))
# Lire le contenu du fichier correspondant à chacune des langues.
contents = []
# Pour chacune des langues, on crée une liste qui contient toutes les
# lignes du fichier correspondant, et on met ces listes dans une liste
# avec tout le contenu.
for language in languages:
with open(languages_and_files[language], 'r', encoding='utf8') as c:
contents.append(c.read().splitlines())
# On renvoie une contruction "tuple" qui contient la liste des langues
# et la liste avec les listes de lignes correspondantes.
return (languages, contents)
def prepare_units(contents):
'''Jumeler les phrases correspondantes dans chacune des langues.'''
# Le '*' devant la variable contents est un "unpacking operator".
# Il sépare la liste de listes de la variable `contents` en listes
# individuelles.
# La fonction `.zip()` se charge en suite de jumeler les lignes de chacune des listes.
units = zip(*contents)
return units
def create_tmx_document():
'''Créer la structure de base de la TMX.'''
# Créer la racine.
tmx = objectify.Element('tmx', {'version': '1.4'})
# Dictionnaire pour définir les attributs requis de l'élément <header>.
# La valeur de l'attribut `srclang` est récupérée depuis la variable
# SOURCE_LANG.
tmxheader = {'creationtool':'Utilitaire stage 2023',
'creationtoolversion': '0.3', 'datatype': 'plaintext',
'segtype': 'sentence', 'adminlang': 'en-US',
'srclang': SOURCE_LANG, 'o-tmf': 'text files'}
# Créer les éléments `<header>` et `<body>`.
# On utiliser le dictionnaire ci-dessus pour ajouter tous les attributs
# requis à l'élément `<header`
header = objectify.SubElement(tmx, 'header', tmxheader)
body = objectify.SubElement(tmx, 'body')
return tmx
def make_tu(languages, unit):
'''Créer un élément <tu> contenant les <tuv> et <seg> pour toutes les langues d'un segment.'''
tu = objectify.Element('tu')
# On utilise la fonction `enumerate()` fournit un compte des itérations
# en plus de la valeur de l'itérateur. Ici, à chaque itération, on obtient
# le compte correspondant à la langue du `<tuv`, et le code de cette langue.
for l, language in enumerate(languages):
# Définir et ajouter l'élément <tuv>
tuv = objectify.SubElement(tu, 'tuv', {XMLLANG: language})
# Définir et ajouter l'élément <seg>
# On ne peut pas assigner directement l'attribut `@text` avec la
# classe `objectify`.
# Syntaxe pour insérer le texte trouvée ici:
# https://stackoverflow.com/a/2151303/8123921
# On utilise le compte pour récupérer le texte de la phrase
# correspondant à la langue du `<tuv>` dans le tuple de la
# variable `unit`.
tuv.seg = objectify.E.seg(unit[l])
return tu
def build_final_tree(tmx):
'''Créer l'arbre XML au complet pour la TMX.'''
# Créer l'arbre
# Insérer le document TMX dans un `ElementTree` confère certains avantages
# au niveau de l'écriture du fichier.
tmxtree = et.ElementTree(tmx)
# Ajouter le DTD au niveau du système.
tmxtree.docinfo.system_url = 'tmx14.dtd'
return tmxtree
def save_tmx(basename, tmxtree):
'''Définir le chemin et le nom complet du fichier TMX, et le sauvegarder.'''
# Définir le chemin et nom du fichier.
tmxext = '.tmx'
tmxname = basename + tmxext
tmxpath = Path(CORPUSPATH.parent/'fichier_tmx')
# Créer le chemin s'il n'existe pas.
if not tmxpath.exists():
tmxpath.mkdir(parents=True, exist_ok=True)
tmxfile = Path(tmxpath/tmxname)
# Écrire le fichier
objectify.deannotate(tmxtree, cleanup_namespaces=True)
tmxtree.write(tmxfile, encoding='utf-8',
xml_declaration=True, pretty_print=True)
## Programme principal ##
if __name__ == '__main__':
# Récupérer les langues et le contenu des fichiers
languages_and_files = get_languages_and_files(CORPUSPATH)
languages, contents = read_content_by_language(languages_and_files)
# Définir le nom de base du fichier
basename = languages_and_files[SOURCE_LANG].stem.strip('_'+SOURCE_LANG)
# Créer un document TMX
tmx = create_tmx_document()
# Jumeler les unités de traduction
units = prepare_units(contents)
# Créer les éléments `<tu>` avec les `<tuv>` contenant le texte des
# segments pour chacune des langues.
for unit in units:
tu = make_tu(languages, unit)
tmx.body.append(tu)
# Compléter l'arbre XML
tmxtree = build_final_tree(tmx)
# Sauvegarder le fichier TMX
save_tmx(basename, tmxtree)

View File

@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
from pathlib import Path
import xml.etree.ElementTree as et
# Définir les constantes
# Valeurs à saisir via une interface graphique dans la version finale.
CORPUSPATH = Path(Path().cwd()/'fichiers_texte')
SOURCE_LANG = 'fr'
def get_languages_and_files(corpuspath):
'''Récupérer les fichiers et leur langues.'''
textfiles = corpuspath.glob('*.txt')
languages_and_files = {l.stem.rsplit('_')[1]:l for l in textfiles}
return languages_and_files
def read_content_by_language(languages_and_files):
'''Lire le contenu du fichier pour chacune des langues'''
# Créer une liste de language avec la langue source en tête
languages = list(languages_and_files.keys())
languages.insert(0,languages.pop(languages.index(SOURCE_LANG)))
# Lire le contenu du fichier correspondant à chacune des langues
contents = []
for language in languages:
with open(languages_and_files[language], 'r', encoding='utf8') as text:
contents.append(text.read().splitlines())
return (languages, contents)
def prepare_units(contents):
'''Jumeler les phrases correspondantes dans chacune des langues.'''
units = zip(*contents)
return units
def create_tmx_document():
'''Créer la structure de base de la TMX.'''
# Créer l'élément racine
tmx = et.Element('tmx', {'version': '1.4'})
# Définir les attributs de l'élément <header>
tmxheader = {'creationtool':'Utilitaire stage 2023',
'creationtoolversion': '0.3', 'datatype': 'plaintext',
'segtype': 'sentence', 'adminlang': 'en-US',
'srclang': SOURCE_LANG, 'o-tmf': 'text files'}
# Créer les éléments <header> et <body>
header = et.SubElement(tmx, 'header', tmxheader)
body = et.SubElement(tmx, 'body')
return tmx
def make_tu(languages, unit):
'''Créer un élément <tu> contenant les <tuv> et <seg> pour toutes les langues d'un segment.'''
tu = et.Element('tu')
for l, language in enumerate(languages):
# Définir et ajouter l'élément <tuv>
tuv = et.SubElement(tu, 'tuv', {'xml:lang': language})
# Définir et ajouter l'élément <seg>
seg = et.SubElement(tuv, 'seg')
seg.text = unit[l]
return tu
def build_final_tree(tmx):
'''Créer l'arbre XML au complet pour la TMX.'''
# Créer l'arbre
tmxtree = et.ElementTree(tmx)
return tmxtree
def save_tmx(basename, tmxtree):
'''Définir le chemin et le nom complet du fichier TMX, et le sauvegarder.'''
# Définir le chemin et nom du fichier.
tmxext = '.tmx'
tmxname = basename + '_xml_etree' + tmxext
tmxpath = Path(CORPUSPATH.parent/'sortie_xml_etree')
# Créer le chemin s'il n'existe pas.
if not tmxpath.exists():
tmxpath.mkdir(parents=True, exist_ok=True)
tmxfile = Path(tmxpath/tmxname)
## Écrire le fichier
# Écrire la déclaration et le doctype dans le fichier
# Solution adaptée de la réponse suivante sur Stack Overflow :
# https://stackoverflow.com/a/8868551/8123921
xmldoc = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE tmx SYSTEM "tmx14.dtd">\n'
# Écrire le fichier
with open(tmxfile, 'wb') as tree:
tree.write(xmldoc.encode('utf-8'))
et.indent(tmxtree)
tmxtree.write(tree, 'utf-8')
if __name__ == '__main__':
# Récupérer les langues et le contenu des fichiers
languages_and_files = get_languages_and_files(CORPUSPATH)
languages, contents = read_content_by_language(languages_and_files)
# Définir le nom de base du fichier
basename = languages_and_files[SOURCE_LANG].stem.strip('_'+SOURCE_LANG)
# Créer un document TMX
tmx = create_tmx_document()
# Jumeler les unités de traduction
units = prepare_units(contents)
# Créer les éléments `<tu>` avec les `<tuv>` contenant le texte des
# segments pour chacune des langues.
for unit in units:
tu = make_tu(languages, unit)
tmx[1].append(tu)
# Compléter l'arbre XML
tmxtree = build_final_tree(tmx)
# Sauvegarder le fichier TMX
save_tmx(basename, tmxtree)