Про локализацию листингов (кода) в книгах
Про локализацию кода в стайлгайдах ИТ-компаний написано скупо. И то, что написано, мягко говоря, не учитывает массу edge cases.
Темы локализации промптов там нет вообще. А я на эти грабли наступил, когда переводил книгу Томажа Братанича «Основы GraphRAG. Улучшенный RAG на базе графов знаний».
Подробно про ту проблематику я сейчас расписывать не буду. Косвенно я её упомянул в телеграм-группе «Дебагеры Read IT Club» . Вот цитата:
Под листингами я понимаю следующие вещи:
1. Фрагменты кода / результаты его выполнения. 2. Логи/выводы команд. 3. Промпты/инференсы/выводы (в т.ч. системные и для взаимодействия с MCP). 4. Сообщения об ошибках.
Может, ещё что-то забыл — пожалуйста, дополните. Но, думаю, направление мыслей понятно.
С одной стороны, нельзя оставлять читателя без перевода — ему может быть непонятно.
С другой стороны, переводить даже значения строковых переменных опасно. Вдруг автору захочется посчитать в них слова, буквы или байты, отсортировать их или использовать строковые значения в качестве заголовков полей в БД (типаorders). Плюс нельзя упускать из виду такой момент: если вы в первой главе что-то перевели по определённому паттерну, то и до конца книги придётся переводить так же. Или не переводить вообще.
С промптами тоже всё неоднозначно. То, что прекрасно работает на английском в одной модели, может работать совсем не так — или совсем никак — на русском в этой же модели. И уж тем более в другой: мало ли на каких тренировочных сетах они выращивались.
С выводами выполнения команд тоже непросто. Вроде как там можно перевести, но надо, видимо, какое-то предупреждение для читателя выводить: этот перевод понарошку, в реальности он всё равно увидит на экране английские слова. Потому что этот шелл, справка или утилита на русский никогда не переводились — и вряд ли кому захочется оплачивать такой перевод.
Жарких дискуссий мой вопрос не вызвал. Но сейчас я работаю над переводом книги Go In Action , и мне этот вопрос надо было как-то решать.
Поэтому, когда я в конце месяца сдавал наработку, он неизбежно возник в обсуждении с заказчиком.
Заказчик предложил переводить строковые значения там, где это безобидно. Например, если в книге даётся так:
...
// Look at the text one byte at a time.
for i := 0; i < len(text); i++ {
if text[i] == ' ' {
numSpaces++
}
}
fmt.Println("Found", numSpaces+1, "words")
}То в переводе это должно выглядеть так:
...
// Просматриваем текст по одному байту за раз.
for i := 0; i < len(text); i++ {
if text[i] == ' ' {
numSpaces++
}
}
fmt.Println("Найдено", numSpaces+1, "слов(а)")
}Или если в командной строке вывод выглядит так:
$ ./wordcount Found 4 words
То в переводе должен выглядеть так:
$ ./wordcount Найдено 4 слов(а)
На первый взгляд логично, и я тоже раньше так думал. Но сейчас я возразил клиенту, так как, покодив немного сам, я понял, что перевод безопаснее ставить в инлайновые комментарии. Вот так:
...
// Просматриваем текст по одному байту за раз.
for i := 0; i < len(text); i++ {
if text[i] == ' ' {
numSpaces++
}
}
fmt.Println("Found", numSpaces+1, "words") // Найдено <N> слов(а)
}$ ./wordcount Found 4 words # Найдено 4 слова
- Это же не код ("Found 4 words"), а вывод программы. Так? - Да, так. Только вы уверены, что дальше по тексту автору не захочется, например, превратить переведённое слово из строкового значения в переменную? В английском это запросто. Допустим, имеем такой листинг:
func main() {
if len(os.Args) < 2 {
log.Println("need to provide at least one filename!")
os.Exit(1)
}
}func main() {
if len(os.Args) < 2 {
log.Println ("Нужно предоставить хотя бы одно имя файла!")
os.Exit(1)
}
}то уже на следующем листинге мы получаем шах и мат:
for _, filename := range os.Args[1:] {
file, err := os.Open(filename)
}Видите, да? "filename" внезапно из строкового значения превратился в переменную filename. И что мы теперь будем делать с нашим "имя файла"?
И, чтобы далеко не отходить, рассмотрим наш исходный пример, где мы имеем те же метаморфозы со словом words:
func main() {
text := "let's count some words!"
words := strings.Fields(text)
fmt.Println("Found", len(words), "words")
}Если мы начнем строковые значения переводить, а переменные нет (а их точно нельзя переводить), мы и читателя запутаем, и сами запутаемся, и логику программы запросто сломаем.
Поэтому, чтобы не ломать эту логику, я и предлагаю все переводы давать в инлайновых комментариях (или не давать вообще):
func main() {
text := "let's count some words!" // давайте посчитаем слова!
words := strings.Fields(text)
fmt.Println("Found", len(words), "words") // Найдено <N> слов(а)
}