комментарии и сообщения переведены на английский

- комментарии, тексты ошибок, вывод CLI и сообщения тестов теперь на английском
- по-русски остались только литералы словаря ru и содержимое фикстур: это
  данные под проверкой, а не текст инструмента
- согласование числительных в итоге упростилось до английского plural
This commit is contained in:
av
2026-07-27 10:04:27 +03:00
parent 0b8cc125b3
commit b2d07ae55d
17 changed files with 548 additions and 524 deletions
+53 -48
View File
@@ -1,12 +1,14 @@
// Package lang держит словари языка конвенций: слова, которыми записаны
// модальность правила, метки его блоков и связки сценарного блока.
// Package lang holds the vocabularies of the conventions language: the words
// that spell out a rule's modality, the marks of its blocks, and the
// connectives of a scenario block.
//
// Словарь — свойство версии языка и естественного языка набора, а не самого
// набора: версия 1 по-русски задаёт один и тот же список слов в любом
// репозитории, и повторять его в каждом манифесте незачем. Пока спецификация
// языка живёт вместе с каноном, словари лежат здесь; когда она уедет в
// отдельный репозиторий со своими файлами словарей, источником станут они, а
// форма Vocabulary и все проверки поверх неё останутся прежними.
// A vocabulary belongs to a language version and to the suite's natural
// language, not to the suite itself: version 1 in Russian names the same words
// in every repository, and repeating that list in every manifest buys nothing.
// While the language specification lives together with the canon, the
// vocabularies live here; once it moves to its own repository with vocabulary
// files of its own, those become the source, and the Vocabulary type together
// with every check built on it stays as it is.
package lang
import (
@@ -17,9 +19,10 @@ import (
"unicode/utf8"
)
// Level — ступень шкалы обязательности. Ступеней пять в четырёх категориях
// ISO/IEC Directives, Part 2; какими словами они названы — параметр
// естественного языка, а сама шкала одна на все словари.
// Level is a step on the scale of obligation. There are five steps in the four
// categories of ISO/IEC Directives, Part 2; which words name them is a
// parameter of the natural language, while the scale is one for all
// vocabularies.
type Level int
const (
@@ -30,26 +33,26 @@ const (
Permission
)
// String даёт имя ступени для сообщений об ошибках — не слово словаря, а роль.
// String names the step for diagnostics — the role, not the vocabulary word.
func (l Level) String() string {
switch l {
case Requirement:
return "требование"
return "requirement"
case Prohibition:
return "запрет"
return "prohibition"
case Recommendation:
return "рекомендация"
return "recommendation"
case RecommendationAgainst:
return "рекомендация против"
return "recommendation against"
case Permission:
return "разрешение"
return "permission"
}
return "неизвестная ступень"
return "unknown level"
}
// Mark — метка блока правила. Метки обязательности не задают, а размечают:
// что здесь обоснование, что иллюстрация, что запись о механизации, что
// заглушка на месте снятого правила.
// Mark labels a block of a rule. Marks set no obligation, they only say what
// this is: a rationale, an illustration, a note about mechanization, a stub in
// place of a retired rule.
type Mark int
const (
@@ -62,20 +65,20 @@ const (
func (m Mark) String() string {
switch m {
case Rationale:
return "обоснование"
return "rationale"
case Examples:
return "примеры"
return "examples"
case Mechanized:
return "механизация"
return "mechanized"
case Retired:
return "снятое правило"
return "retired"
}
return "неизвестная метка"
return "unknown mark"
}
// Connective — служебное слово сценарного блока. В строку о версии языка эти
// слова не входят и под проверку «модальные слова вне правил» не подпадают:
// обязательности они не задают, только структуру.
// Connective is a service word of a scenario block. Such words stay out of the
// language version line and out of the "modal words outside rules" check: they
// set no obligation, only structure.
type Connective int
const (
@@ -85,7 +88,7 @@ const (
Or
)
// Vocabulary — словарь одной версии языка на одном естественном языке.
// Vocabulary is the vocabulary of one language version in one natural language.
type Vocabulary struct {
Version int
Code string
@@ -94,19 +97,19 @@ type Vocabulary struct {
Scenario map[string]Connective
}
// Modal сообщает ступень слова, если слово принадлежит шкале этого словаря.
// Modal reports the step of a word if the word belongs to this scale.
func (v Vocabulary) Modal(word string) (Level, bool) {
l, ok := v.Modals[word]
return l, ok
}
// Mark сообщает роль метки, если слово принадлежит меткам этого словаря.
// Mark reports the role of a mark if the word belongs to these marks.
func (v Vocabulary) Mark(word string) (Mark, bool) {
m, ok := v.Marks[word]
return m, ok
}
// Word возвращает слово, которым в этом словаре записана ступень.
// Word returns the word this vocabulary uses for a step.
func (v Vocabulary) Word(l Level) string {
for w, got := range v.Modals {
if got == l {
@@ -116,7 +119,7 @@ func (v Vocabulary) Word(l Level) string {
return ""
}
// MarkWord возвращает слово, которым в этом словаре записана метка.
// MarkWord returns the word this vocabulary uses for a mark.
func (v Vocabulary) MarkWord(m Mark) string {
for w, got := range v.Marks {
if got == m {
@@ -126,9 +129,9 @@ func (v Vocabulary) MarkWord(m Mark) string {
return ""
}
// Lead возвращает слово словаря, которым начинается text, и его длину.
// Длиннейшее совпадение выигрывает: «НЕ ДОЛЖЕН» не должен читаться как
// «ДОЛЖЕН», а метка с датой («СНЯТО 2026-07-26») — как метка без неё.
// Lead returns the vocabulary word that opens text. The longest match wins:
// "MUST NOT" must not be read as "MUST", and a mark carrying a date
// ("RETIRED 2026-07-26") must not be read as a different mark.
func (v Vocabulary) Lead(text string) (string, bool) {
best := ""
for _, w := range v.Words() {
@@ -143,8 +146,8 @@ func (v Vocabulary) Lead(text string) (string, bool) {
return best, best != ""
}
// Words перечисляет все слова словаря, которые язык объявляет в строке о
// версии: ступени шкалы и метки. Связки сценария сюда не входят.
// Words lists every word the language names in its version line: the steps of
// the scale and the marks. Scenario connectives are not among them.
func (v Vocabulary) Words() []string {
words := make([]string, 0, len(v.Modals)+len(v.Marks))
for w := range v.Modals {
@@ -157,8 +160,8 @@ func (v Vocabulary) Words() []string {
return words
}
// hasWordPrefix проверяет, что text начинается со слова w и слово на этом
// кончается: «ДОЛЖЕНСТВОВАНИЕ» словом ДОЛЖЕН не является.
// hasWordPrefix reports whether text starts with word w and the word ends
// there: "MUSTARD" is not the word "MUST".
func hasWordPrefix(text, w string) bool {
if !strings.HasPrefix(text, w) {
return false
@@ -171,8 +174,8 @@ func hasWordPrefix(text, w string) bool {
return !unicode.IsLetter(r) && !unicode.IsDigit(r)
}
// registry — словари, известные бинарю. Ключ верхнего уровня — версия языка,
// вложенный — код естественного языка набора.
// registry holds the vocabularies known to the binary. The outer key is the
// language version, the inner one the code of the suite's natural language.
var registry = map[int]map[string]Vocabulary{
1: {
"ru": {
@@ -224,21 +227,23 @@ var registry = map[int]map[string]Vocabulary{
},
}
// Lookup выдаёт словарь версии языка на указанном естественном языке.
// Lookup returns the vocabulary of a language version in the given natural
// language.
func Lookup(version int, code string) (Vocabulary, error) {
byCode, ok := registry[version]
if !ok {
return Vocabulary{}, fmt.Errorf("версия языка %d инструменту неизвестна, известны: %s", version, versions())
return Vocabulary{}, fmt.Errorf("language version %d is unknown to the tool; known versions: %s", version, versions())
}
v, ok := byCode[code]
if !ok {
return Vocabulary{}, fmt.Errorf("словарь %q для версии языка %d инструменту неизвестен, известны: %s", code, version, codes(version))
return Vocabulary{}, fmt.Errorf("vocabulary %q of language version %d is unknown to the tool; known: %s", code, version, codes(version))
}
return v, nil
}
// Foreign перечисляет слова чужих словарей той же версии — те, по которым
// видно смесь словарей. Слова, совпадающие с собственными, отброшены.
// Foreign lists the words of the other vocabularies of the same version — the
// ones that give away a mixture of vocabularies. Words that coincide with the
// suite's own are dropped.
func Foreign(version int, code string) map[string]string {
own, err := Lookup(version, code)
if err != nil {
+9 -8
View File
@@ -31,12 +31,12 @@ func TestLeadTakesLongestMatch(t *testing.T) {
got, ok := v.Lead(tc.text)
if tc.want == "" {
if ok {
t.Errorf("Lead(%q) = %q, ждали, что слово не найдётся", tc.text, got)
t.Errorf("Lead(%q) = %q, wanted no word to be found", tc.text, got)
}
continue
}
if !ok || got != tc.want {
t.Errorf("Lead(%q) = %q, %v; ждали %q", tc.text, got, ok, tc.want)
t.Errorf("Lead(%q) = %q, %v; wanted %q", tc.text, got, ok, tc.want)
}
}
}
@@ -44,30 +44,31 @@ func TestLeadTakesLongestMatch(t *testing.T) {
func TestForeignExcludesOwnWords(t *testing.T) {
foreign := lang.Foreign(1, "ru")
if len(foreign) == 0 {
t.Fatal("для русского словаря не нашлось ни одного чужого слова")
t.Fatal("the Russian vocabulary yielded no foreign word at all")
}
if code, ok := foreign["MUST"]; !ok || code != "en" {
t.Errorf("MUST должно опознаваться как слово словаря en, получили %q, %v", code, ok)
t.Errorf("MUST should be recognized as a word of the en vocabulary, got %q, %v", code, ok)
}
v, err := lang.Lookup(1, "ru")
if err != nil {
t.Fatal(err)
}
for word := range foreign {
if _, own := v.Modal(word); own {
t.Errorf("слово %q собственное, а попало в чужие", word)
t.Errorf("the word %q is the suite's own, yet landed among the foreign ones", word)
}
if _, own := v.Mark(word); own {
t.Errorf("метка %q собственная, а попала в чужие", word)
t.Errorf("the mark %q is the suite's own, yet landed among the foreign ones", word)
}
}
}
func TestUnknownVersionAndCode(t *testing.T) {
if _, err := lang.Lookup(99, "ru"); err == nil {
t.Error("неизвестная версия языка принята без ошибки")
t.Error("an unknown language version was accepted without an error")
}
if _, err := lang.Lookup(1, "xx"); err == nil {
t.Error("неизвестный словарь принят без ошибки")
t.Error("an unknown vocabulary was accepted without an error")
}
}