Go to the first, previous, next, last section, table of contents.


프로그램 소스 준비하기

프로그래머에게 해당되는 일로, C 소스 코드를 바꾸는 일은 3가지로 나눌 수 있다. 첫째로, 메세지 번역이 필요한 모든 모듈에 지역화 함수들을 알려줘야 한다. 두번째, 프로그램이 초기화할 때, 보통 main 함수에서 GNU gettext가 제대로 작동하도록 시작해야 한다. 마지막으로, 프로그램 내에 번역이 필요한 문자열이 무엇인지 판단하고 표시한다.

필요한 GNU gettext 파일들을 사용할 수 있도록, 프로그램들과 패키지를 준비해 놓았다면, 그리고 `Makefile' 파일을 준비했다면 (see section 관리자의 관점), 번역될 C 문자열이 있는 각 C 모듈은 다음과 같은 줄이 들어 있게 된다:

#include <libintl.h>

그 외에 C 소스에서 더 바꿔야 할 사항들은 이 장의 나머지 절에서 설명한다.

gettext를 동작시키기

로케일 데이타를 초기화하는 방법은 모든 프로그램에서 거의 동일하다. 아래가 그 예이다:

int
main (argc, argv)
     int argc;
     char argv;
{
  ...
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
  ...
}

PACKAGELOCALEDIR`config.h' 내에 혹은 메이크파일로 정의해야 한다. 지금으로선 더 자세한 정보는 gettext 소스를 참조한다.

LC_ALL을 쓰는 게 적합하지 않을 경우도 있다. LC_ALL은 모든 로케일 범주를 포함하고 특히 LC_CTYPE까지 포함한다. LC_CTYPE 범주는 isalnum 등과 같이 `ctype.h'에 정의되어 있으면서 곳에서 문자의 종류를 판별하는 데 쓰이는데, 어떤 언어를 입력받아 처리하는 프로그램의 경우는 잘못 동작할 수 있다. 예를 들어 @,{c}(c-cedilla 문자)를 사용한 소스 코드는 프랑스에서는 동작하지만 미국에서는 동작하지 않을 수 있다.

어떤 시스템에서는 LC_ALL 로케일을 사용할 경우 scanf 함수를 사용해 숫자를 읽어들일 때 문제가 발생하기도 한다. 표준에서는 여러 가지 형식에 관해 얘기 하지만, "C" 로케일에 있는 형식은 판단할 수 있다. 하지만 어떤 시스템에서는 "C" 로케일 포맷의 숫자를 거부하기도 한다. 어떤 경우에는 그 표시 형식만 보고는 "C" 로케일인지 로케일에 따른 형식인지 알아내는 것도 불가능한 그 표시 형식 자체에 문제가 있을 수도 있다. 이런 상황은 수에서 천 단위로 구분해서 찍는 점이 있을 경우에 발생한다. 어떤 로케일에서는 그 관습에 따라 '.'으로 사용하기도 하는데, 이 점을 "C" 로케일에서 소수점으로 사용된다.

그러므로 어떤 경우에는 위 코드의 LC_ALL 줄을 여러 개의 setlocale 줄로 바꿀 필요가 있다.

{
  ...
  setlocale (LC_CTYPE, "");
  setlocale (LC_MESSAGES, "");
  ...
}

POSIX를 따르는 모든 시스템에서는 LC_CTYPE, LC_COLLATE, LC_MONETARY, LC_NUMERIC, 그리고 LC_TIME을 사용할 수 있다. 몇몇 현대적인 시스템에서는 LC_MESSAGES 로케일도 있는데, 이것은 XPG2를 따르는 좀 오래된 시스템에서는 LC_RESPONSES라고 불리운다.

LC_CTYPE을 바꾸면 <ctype.h> 표준 헤더에 선언된 함수들에도 영향을 끼친다는 데 유의해야 한다. 만약 이들 함수들이 영향을 받지 말아야 할 경우에는 (예를 들어 컴파일러의 파서), C 로케일로 고정되어 있는 대체 함수들을 쓸 수 있다. 이들 함수는 gettext 소스 배포본의 c-ctype,h<c-ctype.c 파일에 들어 있다.

환경변수에 따른 로케일과 C 로케일 사이를 왔다갔다할 수도 있으나, 이런 방법은 보통 피하게 된다. 왜냐하면 먼저 setlocale 호출은 많은 시간을 잡아먹고, 거대한 프로그램 소스의 어디에서 로케일을 전환해야 할 지 애매하고, 또 로케일을 전환하는 것은 쓰레드에 안전하지 못하기 때문이다.

소스 코드에서 어떻게 표시하는가

C 소스 안에 들어 있는 번역이 필요한 문자열은 모두 번역이 가능하다고 표시해야 한다. 이 표시는 인자가 하나뿐인 어떤 함수, 혹은 어떤 전처리 매크로의 인자로 번역될 수 있는 문자열을 넣는 것이다. 번역을 의미하는 그러한 함수 혹은 매크로는 몇 개밖에 없는데, 그 이름을 표시 키워드(marking keyword)라고 한다. 이 표시는 문자열에 대해 무슨 처리를 하려는 부분이 아니라, 문자열 자체에 포함된다. 이 접근 방법이 더 좋다. 대표적인 예는 형식에 맞춰 만들어지는 오류 메세지이다. 이 형식 문자열은 번역되야 함은 물론, 그 형식 내에 `%s'이라고 지정해서 삽입되는 문자열도 번역되야 한다. sprintf의 결과는 아주 여러 가지가 될 수 있겠지만 그 모든 경우를 `error_string_out()' 루틴에서 열거하는 일은 부적절하다.

표시 작업은 두 가지 목표가 있다. 한 가지 목표는 프로그램이 실행할 때 번역문을 얻어내기 위한 것이다. 이 키워드는 필요에 따라, 또 가능한 경우라면 인자가 되는 문자열에 대해 적절한 번역문을 동적으로 리턴하는 루틴으로 바뀔 것이다. 대부분의 지역화 가능한 문자열은 모두 실행직전에, 즉 변수에 들어 있거나 함수의 인자로 주어진다. 하지만 이것이 전부는 아니고, 번역될 수 있는 문자열중 어떤 것은 구조적인 데이타 타입의 초기값으로 놓여진다. See section 번역될 수 있는 문자열의 특별한 경우.

표시 작업의 두번째 목표는 xgettext가 프로그램 소스를 읽으면서 번역될 수 있는 모든 문자열을 뽑아내고 PO 파일 틀을 만들 수 있도록 하는 것이다.

번역가능한 문자열을 표시하는 주요 키워드는 `gettext'이고, 이 키워드는 전체 GNU gettext 패키지를 일컫는 이름이 되었다. `gettext' 키워드를 조금밖에 사용하지 않는 패키지의 경우에는, 그대로 사용하는 편이 쉽다. 하지만, gettext 인터페이스를 자주 사용하는 패키지의 경우, 주요 키워드를 더 짧게, 덜 거슬리게 하는 이름으로 하는 편이 더 편리하다. 실제로 패키지들에서 매우 많은 문자열에 대해 키워드를 써야 할 필요도 있고, 프로그래머는 그 키워드를 보고 이 프로그램 소스가 국제화되어 있다는 사실을 계속해서 생각하게 된다. 게다가, 긴 키워드는 텍스트의 열 수를 더 많이 차지하는 문제가 있기 때문에, 79 혹은 80 열 이내로 유지하려고 노력하는 프로그램 소스의 경우 더 많은 작업이 필요하게 된다.

많은 패키지들은 `_'(한개의 밑줄)을 키워드로 사용하고, `gettext ("Translatable String")' 대신에 `_("Translatable string")'이라고 쓴다. 또 GNU 표준 코딩 규칙에서는 보통 키워드와 괄호를 여는 사이에 빈칸을 하나 넣도록 되어 있지만, 여기서는 특별한 목적이기 때문에 그렇게 쓰지 않는다. 이렇게 번역될 문자열을 지정하는 데 써야 할 부담은 단 세 글자로 줄여진다: 그 글자는 밑줄과 두개의 괄호문자이다. 하지만, 내부적으로는 GNU gettext에서 이런 규칙을 사용하지만, GNU gettext에서 해도 직접 이 규칙을 제공해 주지는 않는다. 진짜 키워드는 `gettext'만 사용할 수 있지만, 아주 쉽게 `gettext' 대신에 `_'를 쓰도록 만들 수 있다.

#include <libintl.h>
#define _(String) gettext (String)

`#include <libintl.h>'만을 사용하는 대신에 이렇게 쓴다.

나중에 관리하기가 상대적으로 쉬울 것이다. 만약 프로그래머가 문자열을 추가하거나 수정할 경우, 그 새로운 혹은 수정한 문자열을 번역해야 하는지 알아봐야 하고, 번역이 필요하다고 생각한 경우 `_()' 안에 넣는다. `"%s: %d"'는 번역이 필요하지 않은 경우의 한 가지 예이다!

번역 가능한 문자열 표시

PO 모드에서, 몇몇 기능들은 번역자를 위한 기능이 아니라 프로그래머를 위한 기능이다. 이 기능들을 이용해 프로그램 소스 내에서 어떤 문자열이 번역가능한 문자열이고, 어떤 문자열이 번역하지 말아야 하는 문자열인지 대화적으로 선택할 수 있다. 그냥 자기가 좋아하는 어떤 에디터를 사용해서 이러한 문자열을 찾아내고, 표시하는 일은 쉬운 작업이다. 그래도, PO 모드를 사용하면 좀 더 편하게 이 작업을 할 수 있다.

또, PO 모드를 이용하면 자기가 프로그래머라고 느끼는 번역자들, 혹은 자기가 번역자라고도 느끼는 프로그래머들은 프로그램 소스에서 번역 가능한 문자열을 표시하면서, 동시에 어떤 언어에 대한 번역문을 만들 수 있다.

여기에 설명하게 될 PO 모드 명령들은, 사용하기 전에 이 명령들이 다루게 될 파일들에 대한 이맥스 태그(tags) 테이블이 만들어져 있어야 한다. 매우 쉽게 할 수 있는 일이다. 셸 윈도우에서 프로젝트의 맨 위 디렉토리로 이동한 다음, 다음과 같은 명령을 실행한다:

etags src/*.[hc] lib/*.[hc]

위 명령은 `src/'`lib/' 디렉토리의 모든 `.h' 파일과 `.c' 파일들에 대해 태그 테이블을 만든다. 이 명령은 언급된 모든 파일을 읽어서, 맨 위 디렉토리에 `TAGS' 파일을 만들 것이고, 이 파일 내용은 이맥스가 이해할 수 있는 형식으로 쓰여진다.

GNU coding standards를 따르는 패키지의 경우, 소스 코드가 들어 있는 모든 파일에 대해 태그 파일을 만드는, tags 혹은 TAGS라는 타겟이 있다.

일단 `TAGS' 파일이 준비되면, 다음 명령들을 이용해 소스코드 내에 있는 번역될 수 있는 문자열을 편하게 표시할 수 있다. 하지만 이 명령은 PO 파일 윈도우에서 사용해야 하는데, PO 파일이 아예 없을 수도 있다. 이건 전혀 문제가 안된다. 이 명령을 쓰기 위해 새롭게 아무것도 쓰여 있지 않은 비어 있는 PO 파일을 열면 되기 때문이다. 이 비어 있는 PO 파일은 프로그램 소스에서 번역될 수 있는 문자열을 표시함에 따라 서서히 내용이 채워질 것이다.

,
프로그램 소스에서 번역할 후보가 될 문자열을 찾는다.
M-,
최근의 문자열을 `_()'로 표시한다.
M-.
최근의 문자열을 가능한 여러 개의 키워드 중의 하나로 표시한다. prefix와 함께 이 명령을 쓰면 이 키워드를 바꿀 수 있다.

,(po-tags-search) 명령은 번역할 후보 문자열을 찾은 다음, 또 다른 이맥스 윈도우에 그 문자열이 윈도우의 맨 위에 오도록 그 프로그램 소스를 표시한다. 만약 문자열이 한 윈도우에 들어가기 너무 큰 경우에는 마지막 부분이 보이도록 위치한다. 어떤 경우든, 커서는 PO 파일 윈도우에 그대로 남아 있다. 보여준 문자열을 다른 언어로 번역할 수 있다고 생각되면, M-, 혹은 M-.로 표시할 수 있다. 그렇지 않으면, ,을 눌러서 이 문자열을 무시하고 다음 문자열로 넘어갈 수 있다.

3자 이상의 문자열은 번역의 후보로 취급한다. 두 자까지의 문자열도 알파벳이 특수문자보다 더 많은 경우 후보로 취급한다. 이 명령은 특수문자로만 이루어진 문자열이나, 단 한글자로 된 문자열은 후보로 취급하지 않는다. 또 이 명령은 주석문 내의 문자열이나 이미 PO 모드가 알고 있는 (아래에 설명한다) 표시 방법으로 표시된 문자열도 무시한다.

이맥스에서 어떤 `TAGS' 파일을 사용할 지 지정하지 않았다면, 이 명령을 처음 실행할 때, `TAGS' 파일을 하나 지정해야 한다고 미니버퍼에서 알려 준다. 나중에 이맥스 명령어 M-x visit-tags-table로 사용할 `TAGS' 파일을 바꿀 수 있다. 이 명령을 쓰면 사용하고 싶은 정확한 `TAGS' 파일의 이름을 물어볼 것이다. See section `Tag Tables' in The Emacs Editor.

, 명령을 사용할 때마다, 앞에서 찾았던 문자열을 뒤로 한채 계속해서 `TAGS'에 쓰여진 프로그램 소스를 따라서, 더 이상 찾아볼 소스 파일이 없을 때까지 찾기를 계속한다. 하지만, 이 명령에 접두어를 주면 (C-u ,), 이 찾기 작업을 첫 번째 프로그램 소스부터 다시 시작한다; 하지만 이 경우에 방금 번역 가능하다고 표시한 문자열은 자동으로 지나갈 것이다.

, 명령을 사용한다고 해서 다른 이맥스 태그 명령을 스지 못하는 건 아니다. 예를 들어, 보통 tags-search 혹은 tags-query-replace 명령은 , 찾기 순서에는 영향을 주지 않고 사용될 수 있다. 하지만 이 명령어의 구현상의 문제로, 최초의 , 명령(혹은 접두어와 함께 사용한 , 명령)은 보통 이맥스 태그 찾기 명령을 첫번째 태그 파일로 초기화할 것이므로, 이 재초기화 명령은 되도록 쓰지 말아야 한다.

M-,(po-mark-translatable) 명령은 최근에 찾은 문자열을 `_' 키워드로 표시한다. M-.(po-select-mark-and-mark) 명령은 미니버퍼에서 키워드를 타이프한 다음 그 키워드를 문자열을 표시할 키워드로 사용한다. 이 두 명령은 자동으로 방금 표시된 문자열에 대한 번역되지 않은 항목을 새 PO 파일에 만들고, 그 항목을 현재 항목으로 만든다 (번역 작업을 하고 싶다면, 즉시 하는 편이 더 쉽다). 80열보다 더 긴 소스 라인에서 M-, 혹은 M-. 로 프로그램 소스를 고치는 일도 가능하지만 그 줄의 들여쓰기가 달라질 수 있다. O 명령을 PO 모드에서 사용하거나, 또는 다른 윈도우 바꾸는 명령을 이맥스에서 사용해서 프로그램 소스로 이동한 다음 필요한 수정을 하면 된다. 다시 PO 파일 윈도우로 돌아오려면, 예를 들어 다음 문자열을 ,로 계속 찾으려면 일반적인 이맥스 명령을 써야 할 것이다.

M-. 명령은 몇개의 속도 향상을 위한 내부적인 방법이 있어서, 이 키워드를 매번 타이프해야 할 필요는 없다. 첫번째는 추천되는 키워드가 보이고, RET을 눌러서 그 키워드를 그대로 사용한다. 두번째는 키워드의 앞 부분을 다른 것과 혼동되지만 않게 타이프하면 자동으로 쓰고자 하는 명령으로 완성(completion)될 것이다. 이것은 PO 모드가 가능한 모든 키워드를 알고 있어야 한다는 뜻이고, 이 경우 잘못 타이프된 키워드는 받아들이지 않는다.

키워드를 타이프할 때 ?라고 답하면, 이 명령은 모든 알고 있는 키워드들을 보여주고, 거기에서 선택할 수 있다. 이 명령에 접두어를 붙이면 (C-u M-.), 이 명령은 PO 파일 버퍼 혹은 프로그램 소스를 갱신해서, 간단한 키워드 관리를 대신 할 것이다. 이 경우, 이 명령은 키워드를 물어보고, 나중에 M-. 명령을 쓸 때 사용할 수 있는 한 가지 키워드가 된다. 게다가 이 새로운 키워드는 자동으로 추천되는 키워드가 되어 나중의 명령에서 사용할 수 있다. 이미 알려진 키워드를 C-u M-.에 대한 대답으로 타이프하면, 추천되는 키워드를 바꿀 뿐이고 아무것도 하지 않는다.

M-.를 쓸 때 알려진 모든 키워드는, , 명령이 이미 표시된 문자열이 있는 경우 건너 뛰는 일을 할때 이용한다. 많은 PO 파일이 동시에 열려 있고, 각각이 따로따로 키워드를 갖고 있는 경우, 현재 PO 모드에서는 알려진 키워드를 지우는 해결방법이 없고, (q를 사용해) 파일을 종료하고 다시 새로 여는 수밖에 없다. PO 파일이 이맥스 윈도우에 새로 등장할 경우 `gettext'`_'만이 키워드로 알려져 있고, `gettext'M-. 명령의 추천되는 키워드가 된다. 사실 `_'를 추천하는 것은 좋지 못한데, M-, 명령으로 사용할 수 있기 때문이다.

키워드 앞에 오는 특수 주석문

C 프로그램에서 printf와 같은 함수에서도 문자열이 사용된다. 이 형식 문자열에서 특별한 점은 %로 시작하는 형식 지정자가 들어갈 수 있다는 점이다. 다음과 같은 코드가 있다고 가정하자:

printf (gettext ("String `%s' has %d characters\n"), s, strlen (s));

위의 문자열에 대해 가능한 독일어 번역문은 다음과 같이 할 수 있다:

"%d Zeichen lang ist die Zeichenkette `%s'"

독일어를 모른다고 해도 C 프로그래머라면 여기서 뭔가 잘못되었다는 걸 알 수 있을 것이다. 두개의 형식 지정자의 순서가 바뀌었지만, 당연히 printf의 인자의 순서는 바뀌지 않는다. 이 문자열의 길이는 주소로 취급될 것이기 때문에 아마도 문제를 일으킬 것이다.

실행시에 여기서 생기는 오류를 막기 위해 msgfmt 도구는 컴파일 시에 원 문자열의 인자와 번역될 문자열의 인자의 타입과 갯수가 맞는지 검사한다. 만약 순서나 타입이 맞지 않다면 경고 메세지가 나오고 실행시에는 문제를 발생하지 않는다.

위의 독일어 번역에서 단어의 순서를 올바르게 하려면 다음과 같이 써야 한다:

"%2$d Zeichen lang ist die Zeichenkette `%1$s'"

msgfmt의 루틴은 이 특별한 표기를 알고 있다.

모든 문자열이 형식 문자열이 될 필요는 없으므로, msgfmt`.po' 파일내의 모든 문자열을 검사하는 일은 바람직하지 않다. 문자열이 형식 지정자처럼 보이는 걸 포함하면서 printf에 사용되지 않은 경우도 있기 때문에 이렇게 하면 문제를 일으킬 수 있다.

xgettext는 형식 문자열처럼 보이는 메세지들에 특별한 플래그를 붙인다. 여기에 확실한 규칙은 없고, 휴리스틱만 있다. `.po' 파일 내에서 이러한 항목은 #, 주석 줄에 c-format을 써서 표시된다 (see section PO 파일의 형식).

이제 주의깊은 독자는 아직도 문제를 발생할 수 있다고 말할 것이다. 이 휴리스틱이 잘못될 경우도 있다. 그건 사실이고 프로그래머의 결정을 xgettext가 알아차릴 수 있는 방법이 있어야 한다. 만약 gettext 키워드가 있는 줄이나 바로 윗 줄에 xgettext:c-format이라는 단어가 들어 있는 주석문을 xgettext가 찾으면 어떤 경우라도 그 문자열에 c-format 플래그를 붙일 것이다. 이런 종류의 주석문은 xgettext가 어떤 문자열이 형식 문자열인지 알지 못할 경우에만 써야 하므로 미리 테스트되야 한다. 주석문이 gettext 키워드와 같은 줄에 있을 때 번역될 문자열 앞에 와야 한다는 것에 유의하자.

위와 같은 상황은 상당히 자주 발생한다. pritnf 함수는 형식 지정자가 들어 있지 않은 채 자주 쓰인다. 물론 fputs를 사용할 수도 있다. 이런 상황에서 xgettext는 이 문자열이 형식 문자열이라는 걸 알지 못하고 번역문에 형식 지정자가 올 경우는 어떻게 되는가? printf 함수는 또 다른 인수를 읽으려고 할 것이지만 원래 프로그램 코드는 인수가 없을 테니, 인수를 찾을 수 없을 것이다.

xgettext는 물론 반대로 결정을 잘못 내릴 수도 있다. 예를 들어 형식 문자열로 표시된 문자열이 형식 문자열이 아닌 경우이다. 이 경우에 msgfmt은 너무 많은 경고 메세지를 낼 것이고 `.po' 번역을 방해할 것이다. 이렇게 xgettext가 잘못 판단하는 경우를 막는 방법은 위에서 사용된 방법과 비슷하게, xgettext:no-c-format 이라는 문자열이 들어 있는 주석문을 쓰면 된다.

어떤 문자열이 c-format으로 표시되어 있을 경우 사용자가 무엇에 의해 이런 판단이 내려졌는지 알아내는 일은 올바르지 않다. 어떻게 --debug 옵션이 이 문제를 해결할 때 사용되는 지는 See section xgettext 프로그램 실행하기.

번역될 수 있는 문자열의 특별한 경우

주의 깊에 읽은 독자는 번역될 수 있는 문자열을 gettext나 이와 비슷한 것으로 언제나 표시할 수는 없다는 사실을 지적할 것이다. 다음 경우를 생각해 보자:

{
  static const char *messages[] = {
    "some very meaningful message",
    "and another one"
  };
  const char *string;
  ...
  string
    = index > 1 ? "a default message" : messages[index];

  fputs (string);
  ...
}

"a default message"를 표시하는 데는 문제가 없지만 messages에서 초기화된 문자열에 표시하는 것은 불가능하다. 어떻게 해야 하는가? 두가지 작업이 필요하다. 첫째는 xgettext 프로그램이 찾을 수 있도록 문자열을 표시하는 것이고 (see section xgettext 프로그램 실행하기), 두번째는 실행시에 문자열을 표시하기 전에 그 문자열을 번역하는 것이다.

첫번째 작업은 no-op라는 이름의 새로운 키워드를 만들어서 할 수 있다. 두번재는 위의 배열에서 문자열을 접근하는 모든 부분에 표시를 하면 된다. 그러므로 한 가지 해결 방법은 다음과 같다:

#define gettext_noop(String) (String)

{
  static const char *messages[] = {
    gettext_noop ("some very meaningful message"),
    gettext_noop ("and another one")
  };
  const char *string;
  ...
  string
    = index > 1 ? gettext ("a default message") : gettext (messages[index]);

  fputs (string);
  ...
}

fputs에 의해 출력되는 문자열이 어떤 경우에도 번역되는 것을 확인한다. 어떻게 xgettextgettext_noop keyword를 알아내는지는 section xgettext 프로그램 실행하기에 설명되어 있다.

물론 위의 방법이 유일한 방법은 아니다. 다음 방법을 사용할 수도 있다:

#define gettext_noop(String) (String)

{
  static const char *messages[] = {
    gettext_noop ("some very meaningful message",
    gettext_noop ("and another one")
  };
  const char *string;
  ...
  string
    = index > 1 ? gettext_noop ("a default message") : messages[index];

  fputs (gettext (string));
  ...
}

하지만, 이렇게 하면 몇 가지 단점이 있다. 첫째로 프로그래머가 "a default message" 문자열에 대해 gettext_noop를 사용하는 지 신경을 써야 한다는 것이다. 알 수 없는 결과에 대해 gettext를 사용하는 경우는 매우 드물다. 두번째로 이런 해결방법은 GNU gettext의 구조상 효율이 떨어진다.

한 가지 좋은 점은 어떤 경우에도 출력문자열이 정말로 번역되는지 흐름을 분석할 필요가 엇다는 점이다. 하지만, 이 분석은 보통 그리 어렵지 않다. 만약 이러한 상황에 처할 수밖에 없다면, 이 두번째 방법을 사용할 수 있다.


Go to the first, previous, next, last section, table of contents.