Interfaces utilisateurs – Dispositions de clavier bureautique français.
Ce document contient des notes compilées lors du développement d’un pilote clavier qui implémente la norme NF Z71-300 relative à la disposition de clavier bureautique français.
Ce document n’est pas une référence, il contient des erreurs et des approximations.
Copyright © 2019, Cyril Lugan
La norme définit la disposition des touches du module alphanumérique du clavier conformément à la série de normes ISO/CEI 9995 – relative à la disposition des claviers conçus pour la bureautique.
Objectifs par rapport aux dispositions établies par l’usage en France :
- Être adapté à la saisie de la langue française.
- Permettre la saisie des autres Langues de France et langues latines.
- Réduire les disparités entre les différents fabricants de matériels et systèmes d’exploitation.
- Améliorer l’ergonomie tout en évitant de provoquer de difficultés d’appropriation.
- Rendre plus accessibles de nouveaux jeux de caractères utiles pour rédiger des documents spécifiques ou techniques.
Deux dispositions y sont normalisées. Une disposition « AZERTY », destinée à remplacer les dispositions « AZERTY » établies par l’usage. Elle a été développée à l'aide d'algorithmes informatiques. La méthodologie employée (Feit et al. 2018) et son historique (Nancel 2018) y sont présentés à titre d’information.
La disposition « BÉPO » y est également normalisée. Elle privilégie l’ergonomie et la performance à la familiarité avec les dispositions « AZERTY ». Son utilisation est particulièrement adaptée aux méthodes de dactylographie. Ce document contient peu de notes à son sujet, car elle est libre et très bien documentée.
D’après NF Z71-300, § 5.3.
Deux types de mécanismes sont reconnus pour la sélection de groupe et de niveau :
- La sélection de niveau 2 (⇧)
- La sélection de groupe 2 (généralement AltGr ou ⌥)
Le niveau 1 et le groupe 1 sont implicitement sélectionnés lorsqu’aucune sélection n’est active.
D’après NF Z71-300, § 5.4.
Le fonctionnement des touches mortes décrite par ISO/CEI 9995-11 § 5.1 s’applique.
# Supported chars and combinations with dead keys will be added
# when defined along the document.
supported_chars = set()
supported_combinations = {}
D’après NF Z71-300, § 6.2.1, Tableau 5 :
latin_letters = [
('A', 'a'), # U+0041, U+0061 (LETTER A)
('B', 'b'), # U+0042, U+0062 (LETTER B)
('C', 'c'), # U+0043, U+0063 (LETTER C)
('D', 'd'), # U+0044, U+0064 (LETTER D)
('E', 'e'), # U+0045, U+0065 (LETTER E)
('F', 'f'), # U+0046, U+0066 (LETTER F)
('G', 'g'), # U+0047, U+0067 (LETTER G)
('H', 'h'), # U+0048, U+0068 (LETTER H)
('I', 'i'), # U+0049, U+0069 (LETTER I)
('J', 'j'), # U+004A, U+006A (LETTER J)
('K', 'k'), # U+004B, U+006B (LETTER K)
('L', 'l'), # U+004C, U+006C (LETTER L)
('M', 'm'), # U+004D, U+006D (LETTER M)
('N', 'n'), # U+004E, U+006E (LETTER N)
('O', 'o'), # U+004F, U+006F (LETTER O)
('P', 'p'), # U+0050, U+0070 (LETTER P)
('Q', 'q'), # U+0051, U+0071 (LETTER Q)
('R', 'r'), # U+0052, U+0072 (LETTER R)
('S', 's'), # U+0053, U+0073 (LETTER S)
('T', 't'), # U+0054, U+0074 (LETTER T)
('U', 'u'), # U+0055, U+0075 (LETTER U)
('V', 'v'), # U+0056, U+0076 (LETTER V)
('W', 'w'), # U+0057, U+0077 (LETTER W)
('X', 'x'), # U+0058, U+0078 (LETTER X)
('Y', 'y'), # U+0059, U+0079 (LETTER Y)
('Z', 'z'), # U+005A, U+007A (LETTER Z)
('Æ', 'æ'), # U+00C6, U+00E6 (LETTER AE)
('Œ', 'œ'), # U+0152, U+0153 (LIGATURE OE)
('ẞ', 'ß'), # U+1E9E, U+00DF (LETTER SHARP S)
('Þ', 'þ'), # U+00DE, U+00FE (LETTER THORN)
('Ð', 'ð'), # U+00D0, U+00F0 (LETTER ETH)
('Ŋ', 'ŋ'), # U+014A, U+014B (LETTER ENG)
('IJ', 'ij'), # U+0132, U+0133 (LIGATURE IJ)
('Ʒ', 'ʒ'), # U+01B7, U+0292 (LETTER EZH)
('Ə', 'ə'), # U+018F, U+0259 (LETTER SCHWA)
('İ', 'ı'), # U+0130, U+0131 (LETTER I WITH DOT ABOVE, LETTER DOTLESS I)
('ϴ', None), # U+03F4 (GREEK CAPITAL THETA SYMBOL)
(None, 'ʼ'), # U+02BC (MODIFIER LETTER APOSTROPHE)
(None, 'ſ'), # U+017F (LETTER LONG S)
]
for lower, upper in latin_letters:
if lower is not None:
supported_chars.add(lower)
if upper is not None:
supported_chars.add(upper)
assert 'N' in supported_chars
assert 'n' in supported_chars
assert None not in supported_chars
D’après NF Z71-300, § 6.2.2, Tableau 6 :
almost_alphabethical_symbols = [
'&', # U+0026 (AMPERSAND)
'©', # U+00A9 (COPYRIGHT SIGN)
'®', # U+00AE (REGISTERED SIGN)
'™', # U+2122 (TRADE MARK SIGN)
'§', # U+00A7 (SECTION SIGN)
'ª', # U+00AA (FEMININE ORDINAL INDICATOR)
'º', # U+00BA (MASCULINE ORDINAL INDICATOR)
'@', # U+0040 (COMMERCIAL AT)
]
supported_chars.update(almost_alphabethical_symbols)
assert 'ª' in supported_chars
D’après NF Z71-300, § 6.2.3, Tableau 7 :
digits = [
('0', '⁰', '₀'), # U+0030, U+2070, U+2080
('1', '¹', '₁'), # U+0031, U+00B9, U+2081
('2', '²', '₂'), # U+0032, U+00B2, U+2082
('3', '³', '₃'), # U+0033, U+00B3, U+2083
('4', '⁴', '₄'), # U+0034, U+2074, U+2084
('5', '⁵', '₅'), # U+0035, U+2075, U+2085
('6', '⁶', '₆'), # U+0036, U+2076, U+2086
('7', '⁷', '₇'), # U+0037, U+2077, U+2087
('8', '⁸', '₈'), # U+0038, U+2078, U+2088
('9', '⁹', '₉'), # U+0039, U+2079, U+2089
]
for line in digits:
supported_chars.update(line)
assert '7' in supported_chars
assert '⁷' in supported_chars
assert '₇' in supported_chars
D’après NF Z71-300, § 6.2.3, Tableau 8 :
symbols = [
'#', # U+0023 (NUMBER SIGN)
'%', # U+0025 (PERCENT SIGN)
'+', # U+002B (PLUS SIGN)
'/', # U+002F (SOLIDUS)
'<', # U+003C (LESS-THAN SIGN)
'=', # U+003D (EQUALS SIGN)
'>', # U+003E (GREATER-THAN SIGN)
'\\', # U+005C (REVERSE SOLIDUS)
'^', # U+005E (CIRCUMFLEX ACCENT)
'_', # U+005F (LOW LINE)
'`', # U+0060 (GRAVE ACCENT)
'|', # U+007C (VERTICAL LINE)
'~', # U+007E (TILDE)
'°', # U+00B0 (DEGREE SIGN)
'±', # U+00B1 (PLUS-MINUS SIGN)
'¼', # U+00BC (VULGAR FRACTION ONE QUARTER)
'½', # U+00BD (VULGAR FRACTION ONE HALF)
'×', # U+00D7 (MULTIPLICATION SIGN)
'÷', # U+00F7 (DIVISION SIGN)
'‰', # U+2030 (PER MILLE SIGN)
'−', # U+2212 (MINUS SIGN)
'≤', # U+2264 (LESS-THAN OR EQUAL TO)
'≥', # U+2265 (GREATER-THAN OR EQUAL TO)
'≠', # U+2260 (NOT EQUAL TO)
'≃', # U+2243 (ASYMPTOTICALLY EQUAL TO)
'≄', # U+2244 (NOT ASYMPTOTICALLY EQUAL TO)
'√', # U+221A (SQUARE ROOT)
'∞', # U+221E (INFINITY)
'µ', # U+00B5 (MICRO SIGN)
]
supported_chars.update(symbols)
assert '≃' in supported_chars
Le séparateur décimal est la virgule « , » (U+002C).
D’après NF Z71-300, § 6.2.5, Tableau 9 :
greek_letters = {
'A': ('α', 'Α'), # U+03B1, U+0391 (ALPHA)
'B': ('β', 'Β'), # U+03B2, U+0392 (BETA)
'G': ('γ', 'Γ'), # U+03B3, U+0393 (GAMMA)
'D': ('δ', 'Δ'), # U+03B4, U+0394 (DELTA)
'E': ('ε', 'Ε'), # U+03B5, U+0395 (EPSILON)
'Z': ('ζ', 'Ζ'), # U+03B6, U+0396 (ZETA)
'H': ('η', 'Η'), # U+03B7, U+0397 (ETA)
'U': ('θ', 'Θ'), # U+03B8, U+0398 (THETA)
'I': ('ι', 'Ι'), # U+03B9, U+0399 (IOTA)
'K': ('κ', 'Κ'), # U+03BA, U+039A (KAPPA)
'L': ('λ', 'Λ'), # U+03BB, U+039B (LAMDA)
'M': ('μ', 'Μ'), # U+03BC, U+039C (MU)
'N': ('ν', 'Ν'), # U+03BD, U+039D (NU)
'J': ('ξ', 'Ξ'), # U+03BE, U+039E (XI)
'O': ('ο', 'Ο'), # U+03BF, U+039F (OMICRON)
'P': ('π', 'Π'), # U+03C0, U+03A0 (PI)
'R': ('ρ', 'Ρ'), # U+03C1, U+03A1 (RHO)
'S': ('σ', 'Σ'), # U+03C3, U+03A3 (SIGMA)
'W': ('ς', None), # U+03C2 (FINAL SIGMA)
'T': ('τ', 'Τ'), # U+03C4, U+03A4 (TAU)
'Y': ('υ', 'Υ'), # U+03C5, U+03A5 (UPSILON)
'F': ('φ', 'Φ'), # U+03C6, U+03A6 (PHI)
'X': ('χ', 'Χ'), # U+03C7, U+03A7 (CHI)
'C': ('ψ', 'Ψ'), # U+03C8, U+03A8 (PSI)
'V': ('ω', 'Ω'), # U+03C9, U+03A9 (OMEGA)
}
for latin_upper, greek_cases in greek_letters.items():
greek_lower, greek_upper = greek_cases
supported_combinations[('◌µ', latin_upper.lower())] = greek_lower
supported_chars.add(greek_lower)
if greek_upper is not None:
supported_combinations[('◌µ', latin_upper)] = greek_upper
supported_chars.add(greek_upper)
assert supported_combinations[('◌µ', 'd')] == 'δ'
assert supported_combinations[('◌µ', 'D')] == 'Δ'
assert 'δ' in supported_chars
assert 'Δ' in supported_chars
assert None not in supported_chars
Le symbole micro, différent de la lettre grecque Mu, s’obtient en pressant la touche morte des lettres grecques suivie d’elle-même ou de l’espace.
supported_chars.add('µ')
supported_combinations[('◌µ', ' ')] = 'µ'
supported_combinations[('◌µ', "\u00A0")] = 'µ'
D’après NF Z71-300, § 6.2.6, Tableau 10 :
typographical_symbols = [
'!', # U+0021 (EXCLAMATION MARK)
'"', # U+0022 (QUOTATION MARK)
"'", # U+0027 (APOSTROPHE)
'*', # U+002A (ASTERISK)
',', # U+002C (COMMA)
'-', # U+002D (HYPHEN-MINUS)
'.', # U+002E (FULL STOP)
':', # U+003A (COLON)
';', # U+003B (SEMICOLON)
'?', # U+003F (QUESTION MARK)
'¡', # U+00A1 (INVERTED EXCLAMATION MARK)
'·', # U+00B7 (MIDDLE DOT)
'¿', # U+00BF (INVERTED QUESTION MARK)
'‑', # U+2011 (NON-BREAKING HYPHEN)
'–', # U+2013 (EN DASH)
'—', # U+2014 (EM DASH)
'’', # U+2019 (RIGHT SINGLE QUOTATION MARK)
'†', # U+2020 (DAGGER)
'‡', # U+2021 (DOUBLE DAGGER)
'…', # U+2026 (HORIZONTAL ELLIPSIS)
]
supported_chars.update(typographical_symbols)
assert '†' in supported_chars
D’après NF Z71-300, § 6.2.6, Tableau 11 :
matching_typographical_symbols = [
'(', # U+0028 (LEFT PARENTHESIS)
')', # U+0029 (RIGHT PARENTHESIS)
'[', # U+005B (LEFT SQUARE BRACKET)
']', # U+005D (RIGHT SQUARE BRACKET)
'{', # U+007B (LEFT CURLY BRACKET)
'}', # U+007D (RIGHT CURLY BRACKET)
'«', # U+00AB (LEFT-POINTING DOUBLE ANGLE QUOTATION MARK)
'»', # U+00BB (RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK)
'‹', # U+2039 (SINGLE LEFT-POINTING ANGLE QUOTATION MARK)
'›', # U+203A (SINGLE RIGHT-POINTING ANGLE QUOTATION MARK)
'‚', # U+201A (SINGLE LOW-9 QUOTATION MARK)
'‘', # U+2018 (LEFT SINGLE QUOTATION MARK)
'“', # U+201C (LEFT DOUBLE QUOTATION MARK)
'”', # U+201D (RIGHT DOUBLE QUOTATION MARK)
'„', # U+201E (DOUBLE LOW-9 QUOTATION MARK)
]
supported_chars.update(matching_typographical_symbols)
assert '»' in supported_chars
D’après NF Z71-300, § 6.2.6, Tableau 12 :
typographical_spaces = [
"\u0020", # SPACE (espace)
"\u00A0", # NO-BREAK SPACE (espace insécable)
"\u202F", # NARROW NO-BREAK SPACE (espace fine insécable)
"\u2003", # EM SPACE (espace cadratin)
]
supported_chars.update(typographical_spaces)
assert ' ' in supported_chars
- L’espace est utilisée entre deux mots, elle peut être coupée par un retour à la ligne.
- L’espace insécable est utilisée entre deux mots ou avant la ponctuation « : », elle ne peut pas être coupée par un retour à la ligne.
- L’espace fine insécable est utilisée avant les ponctuations « ; ! ? », entre les chevrons « » et les milliers d’un nombre.
- L’espace cadratin est utilisée en typographie anglaise, ou devant une abréviation composée d’une lettre suivie d’un point (comme M.).
D’après NF Z71-300, § 6.2.7, Tableau 13.
currency_symbols = [
'€', # U+20AC (EURO SIGN)
'$', # U+0024 (DOLLAR SIGN)
'£', # U+00A3 (POUND SIGN)
]
supported_chars.update(currency_symbols)
D’après NF Z71-300, § 6.2.7, Tableau 14.
Les autres symboles sont accessibles par la touche morte ¤.
extended_currency_symbols = {
'A': (None, '₳', None, None), # U+20B3
'B': ('₿', '฿', None, None), # U+20BF, U+0E3F
'C': ('¢', '₵', '₢', '₡'), # U+00A2, U+20B5, U+20A2, U+20A1
'D': ('₫', '₯', None, None), # U+20AB, U+20AF
'E': ('₠', None, None, '¤'), # U+20A0, U+00A4
'F': ('ƒ', '₣', None, None), # U+0192, U+20A3
'G': ('₲', None, None, None), # U+20B2
'H': ('₴', None, None, None), # U+20B4
'K': ('₭', None, None, None), # U+20AD
'L': ('₺', '₤', '₾', None), # U+20BA, U+20A4, U+20BE
'M': ('₥', 'ℳ', '₼', None), # U+20A5, U+2133, U+20BC
'N': ('₦', None, None, None), # U+20A6
'P': ('₱', '₧', '₰', None), # U+20B1, U+20A7, U+20B0
'R': ('₽', '₹', '₨', None), # U+20BD, U+20B9, U+20A8
'S': ('₪', '₷', None, None), # U+20AA, U+20B7
'T': ('₸', '₮', None, None), # U+20B8, U+20AE
'Y': ('¥', None, None, None), # U+00A5
'W': ('₩', None, None, None), # U+20A9
}
for symbols in extended_currency_symbols.values():
supported_chars.update(symbols)
supported_chars.remove(None)
assert '₸' in supported_chars
assert '₮' in supported_chars
assert None not in supported_chars
D’après NF Z71-300, § 6.3.1.
supported_diacritics = [
'◌̀', # GRAVE ACCENT
'◌́', # ACUTE ACCENT
'◌̂', # CIRCUMFLEX ACCENT
'◌̃', # TILDE
'◌̄', # MACRON
'◌̆', # BREVE
'◌̇', # DOT ABOVE
'◌̈', # DIAERESIS
'◌̊', # RING ABOVE
'◌̋', # DOUBLE ACUTE ACCENT
'◌̌', # CARON
'◌̦', # COMMA BELOW
'◌̧', # CEDILLA
'◌̨', # OGONEK
'◌̵', # SHORT STROKE OVERLAY
'◌̸', # LONG SOLIDUS OVERLAY
'◌̏', # DOUBLE GRAVE ACCENT
'◌̑', # INVERTED BREVE
'◌̣', # DOT BELOW
'◌̱', # MACRON BELOW
]
D’après NF Z71-300, § 6.3.2, Tableau 15.
from kbdrefs.literate import parse_as_table
# TODO check Ḡḡ, ı
letters_combinations_table = parse_as_table("""
|◌ |◌̀ |◌́ |◌̂ |◌̃ |◌̄ |◌̆ |◌̇ |◌̈ |◌̊ |◌̋ |◌̌ |◌̦ |◌̧ |◌̨ |◌̵ |◌̸ |◌̏ |◌̑ |◌̣ |◌̱ |
|Aa|Àà|Áá|Ââ|Ãã|Āā|Ăă|Ȧȧ|Ää|Åå| |Ǎǎ| | |Ąą| |Ⱥ |Ȁȁ|Ȃȃ|Ạạ| |
|Bb| | | | | | |Ḃḃ| | | | | | | |Ƀƀ| | | |Ḅḅ|Ḇḇ|
|Cc| |Ćć|Ĉĉ| | | |Ċċ| | | |Čč| |Çç| | |Ȼȼ| | | | |
|Dd| | | | | | |Ḋḋ| | | |Ďď| |Ḑḑ| |Đđ| | | |Ḍḍ|Ḏḏ|
|Ee|Èè|Éé|Êê|Ẽẽ|Ēē|Ĕĕ|Ėė|Ëë| | |Ěě| |Ȩȩ|Ęę| |Ɇɇ|Ȅȅ|Ȇȇ|Ẹẹ| |
|Ff| | | | | | |Ḟḟ| | | | | | | | | | | | | |
|Gg| |Ǵǵ|Ĝĝ| |Ḡḡ|Ğğ|Ġġ| | | |Ǧǧ| |Ģģ| |Ǥǥ| | | | | |
|Hh| | |Ĥĥ| | | |Ḣḣ|Ḧḧ| | |Ȟȟ| |Ḩḩ| |Ħħ| | | |Ḥḥ| ẖ|
|Ii|Ìì|Íí|Îî|Ĩĩ|Īī|Ĭĭ|İı|Ïï| | |Ǐǐ| | |Įį|Ɨ | |Ȉȉ|Ȋȋ|Ịị| |
|Jj| | |Ĵĵ| | | | | | | | ǰ| | | |Ɉɉ| | | | | |
|Kk| |Ḱḱ| | | | | | | | |Ǩǩ| |Ķķ| | | | | |Ḳḳ|Ḵḵ|
|Ll| |Ĺĺ| | | | |Ŀŀ| | | |Ľľ| |Ļļ| |Ƚƚ|Łł| | |Ḷḷ|Ḻḻ|
|Mm| |Ḿḿ| | | | |Ṁṁ| | | | | | | | | | | |Ṃṃ| |
|Nn|Ǹǹ|Ńń| |Ññ| | |Ṅṅ| | | | | |Ņņ| | | | |Ňň|Ṇṇ|Ṉṉ|
|Oo|Òò|Óó|Ôô|Õõ|Ōō|Ŏŏ|Ȯȯ|Öö| |Őő|Ǒǒ| | |Ǫǫ| |Øø|Ȍȍ|Ȏȏ|Ọọ| |
|Pp| |Ṕṕ| | | | |Ṗṗ| | | | | | | | | | | | | |
|Rr| |Ŕŕ| | | | |Ṙṙ| | | |Řř| |Ŗŗ| |Ɍɍ| |Ȑȑ|Ȓȓ|Ṛṛ|Ṟṟ|
|Ss| |Śś|Ŝŝ| | | |Ṡṡ| | | |Šš|Șș|Şş| | | | | |Ṣṣ| |
|Tt| | | | | | |Ṫṫ| ẗ| | |Ťť|Țț|Ţţ| |Ŧŧ|Ⱦ | | |Ṭṭ|Ṯṯ|
|Uu|Ùù|Úú|Ûû|Ũũ|Ūū|Ŭŭ| |Üü|Ůů|Űű|Ǔǔ| | |Ųų|Ʉʉ| |Ȕȕ|Ȗȗ|Ụụ| |
|Vv| | | |Ṽṽ| | | | | | | | | | | | | | |Ṿṿ| |
|Ww|Ẁẁ|Ẃẃ|Ŵŵ| | | |Ẇẇ|Ẅẅ| ẘ| | | | | | | | | |Ẉẉ| |
|Xx| | | | | | |Ẋẋ|Ẍẍ| | | | | | | | | | | | |
|Yy|Ỳỳ|Ýý|Ŷŷ|Ỹỹ|Ȳȳ| |Ẏẏ|Ÿÿ| ẙ| | | | | |Ɏɏ| | | |Ỵỵ| |
|Zz| |Źź|Ẑẑ| | | |Żż| | | |Žž| | | |Ƶƶ| | | |Ẓẓ|Ẕẕ|
|Ææ| |Ǽǽ| | |Ǣǣ| | | | | | | | | | | | | | | |
|Ʒʒ| | | | | | | | | | |Ǯǯ| | | | | | | | | |
| ſ| | | | | | | ẛ| | | | | | | | | ẜ| | | | |
""", strip='')
Note: La norme comporte une erreur, Ḟ, ḟ est indiqué à la place de Ḡ, ḡ.
# Make combinations from table
letters_combinations = {}
for line in letters_combinations_table[1:]:
for col, dead_key in enumerate(letters_combinations_table[0][1:], 1):
for case in (0, 1):
letter = line[0][case]
output = line[col][case]
if output != ' ':
letters_combinations[(dead_key.strip(), letter)] = output
supported_combinations.update(letters_combinations)
supported_chars.update(letters_combinations.values())
assert supported_combinations[('◌̸', 'A')] == 'Ⱥ'
assert not ('◌̸', 'a') in supported_combinations
assert 'Ⱥ' in supported_chars
assert '◌̸' not in supported_chars
import unicodedata
# Checks that combined char name contains the base letter name
for combination, output in letters_combinations.items():
dead_key, combined_with = combination
exceptions = ['ı']
if not output in exceptions:
combined_with = unicodedata.name(combined_with)
output = unicodedata.name(output)
assert (combined_with in output), (
'{} is not in {}'.format(combined_with, output))
D’après NF Z71-300, § 6.3.3.1, Tableau 16.
La combinaison d’un chiffre avec l’accent circonflexe produit le chiffre en exposant :
special_chars_from_diacritics = {
('◌̂', '0'): '⁰', # U+2070
('◌̂', '1'): '¹', # U+00B9
('◌̂', '2'): '²', # U+00B2
('◌̂', '3'): '³', # U+00B3
('◌̂', '4'): '⁴', # U+2074
('◌̂', '5'): '⁵', # U+2075
('◌̂', '6'): '⁶', # U+2076
('◌̂', '7'): '⁷', # U+2077
('◌̂', '8'): '⁸', # U+2078
('◌̂', '9'): '⁹', # U+2079
}
La combinaison d’un chiffre avec le caron produit le chiffre en indice :
special_chars_from_diacritics.update({
('◌̌', '0'): '₀', # U+2080
('◌̌', '1'): '₁', # U+2081
('◌̌', '2'): '₂', # U+2082
('◌̌', '3'): '₃', # U+2083
('◌̌', '4'): '₄', # U+2084
('◌̌', '5'): '₅', # U+2085
('◌̌', '6'): '₆', # U+2086
('◌̌', '7'): '₇', # U+2087
('◌̌', '8'): '₈', # U+2088
('◌̌', '9'): '₉', # U+2089
})
D’après NF Z71-300, § 6.3.3.2, Tableau 17. Le tilde et la barre oblique permettent d’accéder aux caractères suivants :
special_chars_from_diacritics.update({
('◌̃', '-'): '≃', # U+2243
('◌̃', '='): '≈', # U+2248
('◌̸', '='): '≠', # U+2260
('◌̸', '≃'): '≄', # U+2244
})
supported_combinations.update(special_chars_from_diacritics)
assert supported_combinations[('◌̂', '7')] == '⁷'
assert supported_combinations[('◌̌', '7')] == '₇'
assert supported_combinations[('◌̸', '≃')] == '≄'
Combinaisons avec l’espace telles que décrites par ISO/CEI 9995-11 § 5.2.
from kbdrefs import iso_cei_9995_11
for space in typographical_spaces:
for dead_key in supported_diacritics:
clone = iso_cei_9995_11.get_spacing_clone(dead_key)
supported_combinations[(dead_key, space)] = clone
assert supported_combinations[('◌̀', ' ')] == '`'
Note: Certains caractères définis dans cette section de la norme ne correspondent pas à ceux définis dans la première partie. Par exemple ⩽ (U+2A7D) est utilisé alors que la première partie définit ≤ (U+2264). Ce document utilise la première partie comme référence.
D’après NF Z71-300, § A.1.1.2, Tableau A.1.
Espaces identifiées par leur nom Unicode.
from kbdrefs.literate import parse_as_list
from kbdrefs.iso_cei_9995_1 import alphanumeric_keys
azerty_group1_level1 = parse_as_list("""
║ @ ║ à ║ é ║ è ║ ê ║ ( ║ ) ║ ‘ ║ ’ ║ « ║ » ║ ' ║ ◌̂ ║
║ a ║ z ║ e ║ r ║ t ║ y ║ u ║ i ║ o ║ p ║ - ║ + ║
║ q ║ s ║ d ║ f ║ g ║ h ║ j ║ k ║ l ║ m ║ / ║ * ║
║ < ║ w ║ x ║ c ║ v ║ b ║ n ║ . ║ , ║ : ║ ; ║
""", row_separator='║')
azerty_group1_level1.append(' ') # Espace
azerty_group1_level2 = parse_as_list("""
║ # ║ 1 ║ 2 ║ 3 ║ 4 ║ 5 ║ 6 ║ 7 ║ 8 ║ 9 ║ 0 ║ " ║ ◌̈ ║
║ A ║ Z ║ E ║ R ║ T ║ Y ║ U ║ I ║ O ║ P ║ – ║ ± ║
║ Q ║ S ║ D ║ F ║ G ║ H ║ J ║ K ║ L ║ M ║ \\║ ½ ║
║ > ║ W ║ X ║ C ║ V ║ B ║ N ║ ? ║ ! ║ … ║ = ║
""", row_separator='║')
azerty_group1_level2.append(' ') # Espace
azerty_group2_level1 = parse_as_list("""
║ ◌̆ ║ § ║ ◌́ ║ ◌̀ ║ & ║ [ ║ ] ║ ◌̄ ║ _ ║ “ ║ ” ║ ° ║ ◌̌ ║
║ æ ║ £ ║ € ║ ® ║ { ║ } ║ ù ║ ◌̇ ║ œ ║ % ║ − ║ † ║
║ θ ║ ß ║ $ ║◌¤ ║◌µ ║◌Eu║ ║ ◌̸ ║ | ║ ∞ ║ ÷ ║ × ║
║ ≤ ║ ʒ ║ © ║ ç ║ ◌̧ ║ ◌̵ ║ ◌̃ ║ ¿ ║ ¡ ║ · ║ ≃ ║
""", row_separator='║')
azerty_group2_level1.append('\u00A0') # Espace insécable
azerty_group2_level2 = parse_as_list("""
║ ◌̑ ║ À ║ É ║ È ║ Ê ║ ◌̋ ║ ◌̏ ║ ║ — ║ ‹ ║ › ║ ◌̊ ║ ║
║ Æ ║ ║ ║ ║ ™ ║ ║ Ù ║ ◌̣ ║ Œ ║ ‰ ║ ‑ ║ ‡ ║
║ ϴ ║ ẞ ║ ║ ║ ║ ◌̱ ║ ║ ║ ║ ║ √ ║ ¼ ║
║ ≥ ║ Ʒ ║ ║ Ç ║ ◌̨ ║ ║ ║ ║ ◌̦ ║ ║ ≠ ║
""", row_separator='║')
azerty_group2_level2.append('\u202F') # Espace fine insécable
azerty_layout = dict(zip(
alphanumeric_keys,
zip(azerty_group1_level1,
azerty_group1_level2,
azerty_group2_level1,
azerty_group2_level2)))
assert azerty_layout['B06'] == ('n', 'N', '◌̃', None)
D’après NF Z71-300, § A.1.1.3, Tableau A.2.
Lorsque le verrouillage des majuscules est activé, le caractère de niveau 2 est sélectionné pour les chiffres et les lettres. La saisie des autres caractères reste inchangée.
azerty_caps_lock = [
# Selected in group 1
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
# Selected in group 2
'À', 'É', 'È', 'Ê', 'Æ', 'Ù', 'Œ', 'Θ', 'ẞ', 'Ʒ', 'Ç'
]
azerty_combinations = {}
azerty_combinations.update(supported_combinations)
D’après NF Z71-300, § A.1.2.1, Tableau A.4.
azerty_combinations.update({
('◌̸', '<'): '≮', # U+226E
('◌̸', '>'): '≯', # U+226F
('◌̸', '≤'): '≰', # U+2270
('◌̸', '≥'): '≱', # U+2271
})
D’après NF Z71-300, § A.1.2.1, Tableau A.5.
azerty_combinations.update({
('◌̃', '<'): '≲', # U+2272
('◌̃', '>'): '≳', # U+2273
})
D’après NF Z71-300, § A.1.2.3.
for letter, symbols in extended_currency_symbols.items():
for key_chars in azerty_layout.values():
if key_chars[1] == letter:
for i, symbol in enumerate(symbols):
if symbol is not None and symbol is not '¤':
azerty_combinations[('◌¤', key_chars[i])] = symbol
break
Note: D’après NF Z71-300, § 6.2.7, Tableau 14. Le symbole « ¤ » devrait être obtenu en combinant la touche morte symbole monétaire et le caractère en groupe 2, niveau 2 de la touche « E ». Cependant la disposition AZERTY ne définit pas de caractère en groupe 2, niveau 2 pour la touche « E ». Ce document remplace cette combinaison par l’espace.
azerty_combinations.update({
('◌¤', ' '): '¤',
('◌¤', "\u00A0"): '¤',
})
D’après NF Z71-300, § A.1.2.4.
extended_latin_letters = {
'A': ('ª', None),
'D': ('ð', 'Ð'),
'E': ('ə', 'Ə'),
'G': ('ŋ', 'Ŋ'),
'O': ('º', None),
'S': ('ſ', None),
'T': ('Þ', 'þ'),
'J': ('ij', 'IJ'),
'I': ('ı', 'İ'),
}
extended_latin_symbols = {
'‘': '‚', # U+201A
'7': '›', # U+203A
'’': '‘', # U+2018
'8': '‹', # U+2039
'«': '„', # U+201E
'9': '»', # U+00BB
'»': '“', # U+201C
'0': '«', # U+00AB
"'": 'ʼ', # U+02BC
}
for letter, extended in extended_latin_letters.items():
extended_lower, extended_upper = extended
azerty_combinations[('◌Eu', letter.lower())] = extended_lower
if extended_upper is not None:
azerty_combinations[('◌Eu', letter)] = extended_upper
for char, symbol in extended_latin_symbols.items():
azerty_combinations[('◌Eu', char)] = symbol
Note: L’espace cadratin n’est pas supporté dans la disposition AZERTY normalisée, alors qu’il est définit dans NF Z71-300, § 6.2.6, Tableau 12. Ce document le rend disponible par la touche morte des caractères européens additionels, comme sur la disposition BÉPO.
azerty_combinations.update({
('◌Eu', ' '): "\u2003",
('◌Eu', "\u00A0"): "\u2003",
})
import re
# Supported chars contains only single codepoint chars
for char in supported_chars:
assert type(char) == str, "{} is not a str".format(char)
assert len(char) == 1, "len({}) is not 1".format(char)
# Supported combinations
# - defines dead keys combined with ◌
# - combines with supported chars
# - outputs single codepoint chars, except for some spacing clones
for combination, output in azerty_combinations.items():
dead_key, combined_with = combination
assert dead_key[0] == '◌', (
"◌ not in {}".format((combination, output)))
assert combined_with in supported_chars, "Cannot combine with {}, not in supported chars".format(combined_with)
if not combined_with in typographical_spaces:
assert len(output) == 1, (
"{} unexpected len".format((combination, output)))
# Combinations are consistent with special combinations
# defined in ISO/CEI 9995-11
for combination, output in azerty_combinations.items():
if combination in iso_cei_9995_11.special_chars_combinations:
expected = iso_cei_9995_11.special_chars_combinations[combination]
assert output == expected, "{} != {}".format(output, expected)
# Codepoints in comments describes char on the same line
import re
with open(MARKDOWN_FILE, 'r') as file:
for line in file:
for codepoint in re.findall(r'U\+[0-9a-fA-F]{4}', line):
char = chr(int(codepoint[2:], 16))
assert char in line , (
"{} not in {}".format(char, line))
# AZERTY supports every chars defined in the document
supported_by_azerty = set()
for chars in azerty_layout.values():
supported_by_azerty.update(chars)
for combination, output in azerty_combinations.items():
dead_key, combined_with = combination
assert dead_key in supported_by_azerty, "Dead key {} is not supported by azerty".format(dead_key)
supported_by_azerty.add(output)
for char in supported_chars:
assert char in supported_by_azerty, "Char {} is not supported by azerty".format(char)
Fiet et al. 2018 : Anna Feit, Mathieu Nancel, Daryl Weir, Gilles Bailly, Maximilian John, et al. Élaboration de la disposition AZERTY modernisée. 2018.
Nancel 2018 : Mathieu Nancel. Historique et méthodologie de la nouvelle disposition de clavier AZERTY. 2018.
NF Z71-300 : Norme AFNOR Z71-300. Interfaces utilisateurs - Dispositions de clavier bureautique français. 2019.