9

I'm using the package gb4e to gloss my linguistic examples. It nicely aligns the words in the original language with its glosses. But this alignment does not extend beyond the first line of glosses. If I want to gloss the words in more than one language, there will be no alignment beyond the first line of glosses. How can I enforce such alignment?

\documentclass{article}
\usepackage{gb4e}
\begin{document}
\begin{exe}
    \ex
    \gll gef hundinum matinn\\
    gje hunden maten\\
    give {the dog} {the food}\\
    gib {dem Hunde} {das Essen}\\
\end{exe}
\end{document}

enter image description here

2
  • Manually should be no problem, but I guess you want it automated (with cookies on the side).
    – Werner
    Jan 26, 2014 at 19:30
  • @Werner Manual solutions will also be appreciated! :)
    – Sverre
    Jan 26, 2014 at 19:35

4 Answers 4

14

The cgloss4e package, which is loaded by gb4e is able to account for an example with two lines of glosses automatically, using the \glll (note the extra l) macro:

\documentclass{article}
\usepackage{gb4e}
\begin{document}
\begin{exe}
    \ex
    \glll gef hundinum matinn\\
    give {the dog} {the food}\\
    gib {dem Hunde} {das Essen}\\
\end{exe}
\end{document}

However, for your example, you require yet one more line of gloss, for which the package is not actually designed. But it's not to hard to add the relevant code. Here I've created a \gliv command to introduce an example with 3 extra gloss lines.

\documentclass{article}
\usepackage{gb4e}
\makeatletter
\newbox\linefour
\newbox\wordfour
\def\gliv%                 % Introduces 4-line text-and-gloss.
   {\begin{flushleft}
     \ifx\@gsingle1% 
        \vskip\baselineskip\def\baselinestretch{1}%
        \@selfnt\vskip-\baselineskip\fi%
    \bgroup
    \foursent
   }
\@ifundefined{eachwordfour}{\let\eachwordfour=\rmfamily}{\relax}   
\gdef\foursent#1\\ #2\\ #3\\ #4\\{% #1 = first line, #2 = second line, #3 = third, #4 = fourth
    \getwords(\lineone,\eachwordone)#1 \\%
    \getwords(\linetwo,\eachwordtwo)#2 \\%
    \getwords(\linethree,\eachwordthree)#3 \\%
    \getwords(\linefour,\eachwordfour)#4 \\%
    \loop\lastword{\eachwordone}{\lineone}{\wordone}%
         \lastword{\eachwordtwo}{\linetwo}{\wordtwo}%
         \lastword{\eachwordthree}{\linethree}{\wordthree}%
         \lastword{\eachwordfour}{\linefour}{\wordfour}%
         \global\setbox\gline=\hbox{\unhbox\gline
                                    \hskip\glossglue
                                    \vtop{\box\wordone   % vtop was vbox
                                          \nointerlineskip
                                          \box\wordtwo
                                          \nointerlineskip
                                          \box\wordthree
                                          \nointerlineskip
                                          \box\wordfour
                                         }%
                                   }%
         \testdone
         \ifnotdone
    \repeat
    \egroup % matches \bgroup in \gloss
   \gl@stop}
\makeatother

\begin{document}
\begin{exe}
    \ex
    \gliv gef hundinum matinn\\
    gje hunden maten\\
    give {the dog} {the food}\\
    gib {dem Hunde} {das Essen}\\
\end{exe}
\end{document}

Alternatively, you can embed ExPex glossed sentences into gb4e examples by using the expex-glossonly package loaded with the [gb4e] option. See this answer for example ExPex glosses.

output of code

5

A manual way of achieving this is possible via tabbing:

enter image description here

\documentclass{article}
\usepackage{gb4e}
\begin{document}
\begin{exe}
    \ex \begin{tabbing}
      give \= dem Hunde \= das Essen \kill
      gef \> hundinum \> matinn \\
      gje \> hunden \> maten\\
      give \> the dog \> the food \\
      gib \> dem Hunde \> das Essen
    \end{tabbing}
\end{exe}
\end{document}

The \kill line contains the longest word across each language translation to mark the tabbing points with \=.

2
  • This solution is really only practical for very short lines. One of the things that the glossing macros do is automatically line up each word independent of the length of the example, so that the glossed lines will wrap as needed.
    – Alan Munn
    Jan 27, 2014 at 2:51
  • @AlanMunn: True. You've done a great job at extending this.
    – Werner
    Jan 27, 2014 at 5:16
1

enter image description here

For the sake of completeness, and for readers who are not bound to a package, this functionality is built-in in expex. It already defines 3 levels of glosses (a, b and c), and you can define your own ones. Customizing their appearance is straightforward. Learn more about the rest of the potentialities of expex's glossing mechanism in the documentation.

\documentclass{article}
\usepackage{expex}
\begin{document}
\ex
 \begingl
 \gla gef hundinum matinn//
 \glb gje hunden maten//
 \glb give {the dog} {the food}//
 \glb gib {dem Hunde} {das Essen}//
 \endgl
\xe

\defineglwlevels{english,german}
\lingset{everygla=\bfseries,everyglenglish=\footnotesize,everyglgerman=\ttfamily} 

\ex
 \begingl
 \gla gef hundinum matinn//
 \glb gje hunden maten//
 \glenglish give {the dog} {the food}//
 \glgerman gib {dem Hunde} {das Essen}//
 \endgl
\xe
\end{document}
1
  • If you don't want to commit to using ExPex for everything, you can use the ExPex glossing macros with other example numbering packages with the expex-glossonly package.
    – Alan Munn
    Oct 24 at 14:54
0

Experimental stripped-down version of a general expl3 macro, \glinlines, that parses an unlimited number of lines and words into word-by-word stacked units, plus an unlimited number of translation/note lines at the end.

It can slot into the gb4e example environment (*/ marks a new line):

\begin{exe}
    \ex
\glinlines{% 
       gef hundinum matinn
*/    gje hunden maten
*/    give {the dog} {the food}
*/    gib {dem Hunde} {das Essen}
}
\end{exe}

hound

With user-definable formatting (with switches) for items pre-pended with ..

MWE

\documentclass{article}
\usepackage{xcolor}
\usepackage{fontspec}
\setmainfont{Noto Serif}
\setsansfont{Noto Sans}
\usepackage{xparse}
\usepackage{gb4e}


%-------------------------------------------------------------
\ExplSyntaxOn

%       \cs_generate_variant:Nn 
%           \seq_gset_split:Nnn 
%           { cno }

        \cs_generate_variant:Nn 
            \seq_gset_split:Nnn 
            { coo }

        \cs_generate_variant:Nn 
        \regex_match:nnT
        { nvT }
        
        
\tl_new:N \g_fc_namespace_tl 

%------------------
%****************************************************
%* procedures
%****************************************************
%------------------
\tl_new:N \g__gloss_intoseqname_tl
\tl_new:N \g__gloss_fromseqname_tl
\int_new:N \g__gloss_clausecount_int
\int_new:N \g__gloss_clauseloop_int
\tl_new:N \g__gloss_clauseloop_tl

\int_new:N \g__gloss_basenamecount_int
\int_new:N \g__gloss_basenameloop_int
\tl_new:N \g__gloss_basenameloop_tl

\int_new:N \g__gloss_wordloop_int
\tl_new:N \g__gloss_wordloop_tl

\int_new:N \g__gloss_maxlinecount_int
\int_new:N \g__gloss_maxwordcount_int

\bool_new:N \g__gloss_vpar_bool
\int_new:N \g__gloss_vpar_int




%------------------
    \cs_set:Npn \gl_functexttrans:n #1 { 
                                        \tex_space:D
                                        \\
                                                        \seq_item:cn
                                                                { g_gloss_glossglosses \int_use:N \g__gloss_maxlinecount_int _seq }
                                                                { #1 }
}

%------------------
    \cs_set:Npn \gl_funcboxall:nnnn #1#2#3#4 { 
    % 1=from seq base name
    % 2=base name loop start
    % 3=base name loop finish
    % 4=words seq 1 (words to box)
    
    
    
% outer loop
\int_set:Nn 
        \g__gloss_basenamecount_int
        { 
                #3      
        }   
    \int_set:Nn
            \g__gloss_basenameloop_int
            { #2 }
            
    \int_do_until:nNnn 
        { \g__gloss_basenameloop_int } > { \g__gloss_basenamecount_int } 
     {
    
    
\tl_set:Nx
     \g__gloss_fromseqname_tl 
     { 
            #1 
            \int_use:N \g__gloss_basenameloop_int
     }
     
\tl_set:Nx \g__gloss_intoseqname_tl 
        { 
            #1 
            \int_use:N \g__gloss_basenameloop_int
            #4
        }


\int_set:Nn 
        \g__gloss_clausecount_int
        { 
                \seq_count:c
                        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }       
        }

% loop start

    \int_set:Nn
            \g__gloss_clauseloop_int
            { 1 }

    \int_set:Nn
            \g__gloss_wordloop_int
            { 1 }
    \int_do_until:nNnn 
        { \g__gloss_wordloop_int } > { \seq_count:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq } } 
     {

%---
            \int_compare:nNnT
                            { \g__gloss_wordloop_int } > { \g__gloss_maxwordcount_int }
                            {
                                    \int_set:Nn
                                            \g__gloss_maxwordcount_int
                                            { \g__gloss_wordloop_int }
                    }
%---


\tl_set:Nx
        \g__gloss_wordloop_tl
        { \int_use:N \g__gloss_wordloop_int }

\cs_if_free:cT
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl\g__gloss_wordloop_tl _box }
        { \box_new:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl\g__gloss_wordloop_tl _box }
        }


    \tl_set:Nx
                \l_tmpc_tl
            {
                \seq_item:cn
                            { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl  _seq }
                            { \int_use:N \g__gloss_wordloop_int }
                }
                
                

%vvv
%    .text  >  .{\mfsposformat text}
        \regex_replace_all:nnN
                { (\.)(\w+) }
                { \1 \cB\{ \c{mfsposformat} \2 \cE\} }
                \l_tmpc_tl

%       :command;    > \command 
        \regex_replace_all:nnN
                { (\:)(\w+)(;) }
                { \c{\2}  }
                \l_tmpc_tl
                
%       :command    >   \command
        \regex_replace_all:nnN
                { (\:)(\w+) }
                { \c{\2}  }
                \l_tmpc_tl
                

        \regex_match:nvT
                { \+ }
                { l_tmpc_tl } 
                { \int_set_eq:NN 
                            \g__gloss_vpar_int 
                            \g__gloss_wordloop_int }
                
        \regex_replace_all:nnN
                { \+ }
                {  }
                \l_tmpc_tl


                
    \hbox_gset:cn 
            { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl\g__gloss_wordloop_tl _box }
            {
                    \tl_use:N
                            \l_tmpc_tl
            }


        
        %============================
        
        \int_incr:N
                    \g__gloss_wordloop_int
                    
        } % loop finish


        \int_incr:N
                    \g__gloss_basenameloop_int
                    
        } % outer loop finish


%+++++++++++++++++++++++++
% stack the boxes

\tex_space:D %=


%\int_set:Nn \g__gloss_vpar_int { 0 }

% outer loop
    \int_set:Nn
            \g__gloss_clauseloop_int
            { 1 }

    \int_set:Nn
            \g__gloss_wordloop_int
            { 1 }

    \int_do_until:nNnn 
        { \g__gloss_wordloop_int } > { \seq_count:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq } } 
     {
    
    

% loop start

\int_set:Nn 
        \g__gloss_basenamecount_int
        { 
                #3      
        }   
    \int_set:Nn
            \g__gloss_basenameloop_int
            { #2 }

\vbox_set_top:Nn
        \l__vpair_box
        { %^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            
    \int_do_until:nNnn 
        { \g__gloss_basenameloop_int } > { \g__gloss_basenamecount_int } 

     {

\tl_set:Nx
     \g__gloss_fromseqname_tl 
     { 
            #1 
            \int_use:N \g__gloss_basenameloop_int
     }
     
\tl_set:Nx \g__gloss_intoseqname_tl 
        { 
            #1 
            \int_use:N \g__gloss_basenameloop_int
            #4
        }

\int_set:Nn 
        \g__gloss_clausecount_int
        { 
                \seq_count:c
                        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }       
        }


\tl_set:Nx
        \g__gloss_wordloop_tl
        { \int_use:N \g__gloss_wordloop_int }

    \box_use:c
{ g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl\g__gloss_wordloop_tl _box }


        %============================
        \int_incr:N
                    \g__gloss_basenameloop_int
        } % loop finish
        
        } %^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        
\hbox_set:Nn
        \l__hunit_box
        {
                    \box_use:N %=
                                \l__vpair_box
                    }   

        \box_use:N %=
                \l__hunit_box 
                
                        \int_compare:nNnTF
                            { \g__gloss_vpar_int  } = { \g__gloss_wordloop_int }
                            {
                                    \\
                                    \int_set:Nn \g__gloss_vpar_int { 0 }
                            }
                {
                            \tex_space:D %=
                            \tex_space:D %=     
                }

                            
                             


                
        \int_incr:N
                    \g__gloss_wordloop_int
        } % outer loop finish


            \int_compare:nNnT
                            { \seq_count:c
                                    { g_gloss_glossglosses \int_use:N \g__gloss_maxlinecount_int _seq }
                             } > { 1 }
                            {
                                \smallskip
                                \int_step_function:nnnN
                                        { 2 } 
                                        { 1 } 
                                        { \seq_count:c
                                    { g_gloss_glossglosses \int_use:N \g__gloss_maxlinecount_int _seq } } 
                                          \gl_functexttrans:n
                                            }
                            
}




%------------------
    \cs_set:Npn \gl_funcsplitloopseq:nnnnn #1#2#3#4#5 { 
    % 1=from seq base name
    % 2=base name loop start
    % 3=base name loop finish
    % 4=by what
    % 5=into seq
    
% outer loop
\int_set:Nn 
        \g__gloss_basenamecount_int
        { 
                #3      
        }   
    \int_set:Nn
            \g__gloss_basenameloop_int
            { #2 }
            
    \int_do_until:nNnn 
        { \g__gloss_basenameloop_int } > { \g__gloss_basenamecount_int } 
     {
    
    
\tl_set:Nx
     \g__gloss_fromseqname_tl 
     { 
            #1 
            \int_use:N \g__gloss_basenameloop_int
     }
     
\tl_set:Nx \g__gloss_intoseqname_tl 
        { 
            #1 
            \int_use:N \g__gloss_basenameloop_int
            #5 
        }

\int_set:Nn 
        \g__gloss_clausecount_int
        { 
                \seq_count:c
                        { g_gloss_ \g_fc_namespace_tl \g__gloss_fromseqname_tl _seq }       
        }

% loop start

    \int_set:Nn
            \g__gloss_clauseloop_int
            { 1 }
    \int_do_until:nNnn 
        { \g__gloss_clauseloop_int } > { \g__gloss_clausecount_int } 
     {

\tl_set:Nx
        \g__gloss_clauseloop_tl
        { \int_use:N \g__gloss_clauseloop_int }

\cs_if_free:cT
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }
        { \seq_new:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }
        }

    \seq_gclear:c 
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }

            \exp_args:Nnnx
    \seq_gset_split:coo 
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }
            { #4 } % separator
            { 
                \seq_item:cn
                            { g_gloss_ \g_fc_namespace_tl \g__gloss_fromseqname_tl  _seq }
                            { \int_use:N \g__gloss_clauseloop_int }
             }

        %--------------------------------
        
        
        %============================
        
        \int_incr:N
                    \g__gloss_clauseloop_int
                    
        } % loop finish


        \int_incr:N
                    \g__gloss_basenameloop_int
                    
        } % outer loop finish

}





%------------------
    \cs_set:Npn \gl_funcsplitseq:nnn #1#2#3 { 
    % 1=from seq
    % 2=by what
    % 3=into seq
    
\tl_set:Nx \g__gloss_fromseqname_tl { #1 }
\tl_set:Nx \g__gloss_intoseqname_tl { #3 }

%\tl_show:N \g__gloss_fromseqname_tl 
%\tl_show:N \g__gloss_intoseqname_tl 

\int_set:Nn 
        \g__gloss_clausecount_int
        { 
                \seq_count:c
                        { g_gloss_ \g_fc_namespace_tl \g__gloss_fromseqname_tl _seq }       
        }

% loop start

    \int_set:Nn
            \g__gloss_clauseloop_int
            { 1 }
    \int_do_until:nNnn 
        { \g__gloss_clauseloop_int } > { \g__gloss_clausecount_int } 
     {

\tl_set:Nx
        \g__gloss_clauseloop_tl
        { \int_use:N \g__gloss_clauseloop_int }

\cs_if_free:cT
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }
        { \seq_new:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }
        }

    \seq_gclear:c 
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }

            \exp_args:Nnnx
    \seq_gset_split:coo 
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl \g__gloss_clauseloop_tl _seq }
            { #2 } 
            { 
                \seq_item:cn
                            { g_gloss_ \g_fc_namespace_tl \g__gloss_fromseqname_tl  _seq }
                            { \int_use:N \g__gloss_clauseloop_int }
             }

        
        \int_incr:N
                    \g__gloss_clauseloop_int
                    
        } % loop finish

}







%------------------
    \cs_set:Npn \gl_funcsplit:nnn #1#2#3 { 
    % 1=into seq
    % 2=what
    % 3=by what

\tl_set:Nx \g__gloss_intoseqname_tl { #1 }
\tl_set:Nx \g__gloss_intoseqwhat_tl { #2 }
\tl_set:Nx \g__gloss_intoseqbywhat_tl { #3 }




\cs_if_free:cT
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }
        { \seq_new:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }
        }

    \seq_gclear:c 
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }

%           \exp_args:Nnnx
    \seq_gset_split:coo 
        { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }
            { #3 } 
            { #2 }


\int_set:Nn 
        \g__gloss_maxlinecount_int
        {
            \seq_count:c
                { g_gloss_ \g_fc_namespace_tl \g__gloss_intoseqname_tl _seq }   
        }

}



%****************************************************
%* main commands
%****************************************************
%--------------------
\NewDocumentCommand { \glinlinesbase } { o m m +m } { 
    % 1=namespace
    % 2=seq
    % 3=sep
    % 4=data

                \IfNoValueTF { #1 } 
                        { \tl_clear:N \g_fc_namespace_tl } 
                        { \tl_gset:Nn \g_fc_namespace_tl { #1 } }

\int_set:Nn \g__gloss_maxlinecount_int { 0 }
    % split into input lines
    \gl_funcsplit:nnn {#2inlines}{#4}{#3}
    % 1=into seq
    % 2=what
    % 3=by what
    \gl_funcsplitseq:nnn 
            {#2inlines}
            {glt}
            {glosses}

    \gl_funcsplitloopseq:nnnnn 
                {glosses}
                {1}
                {
                        \int_use:N
                            \g__gloss_maxlinecount_int
                    }
                {~}
                {words}

\int_set:Nn \g__gloss_maxwordcount_int { 0 }

    \gl_funcboxall:nnnn 
                {glosses}
                {1}
                {
                        \int_use:N
                            \g__gloss_maxlinecount_int
                        }
                {words1}

}





%--------------------
\NewDocumentCommand { \glinlines } { +m } { 
    % 1==data
        \glinlinesbase[gloss]{ex2a}{*/}{#1}
}

\box_new:N \l__vpair_box
\box_new:N \l__hunit_box

%--------------------
\NewDocumentCommand { \mfsposformat } { } { 
            \scshape\color{blue}
}
%--------------------
\NewDocumentCommand { \mfssetposformat } { m } { 
            \renewcommand{\mfsposformat}{#1}
}

\ExplSyntaxOff

\begin{document}


\begin{exe}
    \ex
\glinlines{% end-of-line counts as an extra space
       gef hundinum matinn
*/    gje hunden maten
*/    give {the dog} {the food}
*/    gib {dem Hunde} {das Essen}
}
\end{exe}


\begin{exe}
    \ex
\glinlines{% end-of-line counts as an extra space
       gef hundinum matinn
*/    give.tense hound.case meat.case
*/    gje hunden maten
*/    give {the dog} {the food}
*/    .vrb {.art .noun} {.art .noun}
*/    .cmd .subj .obj
*/    gib {dem Hunde} {das Essen}
glt    One of the daily chores.
glt    Multiple translation/note lines.
glt    Formal literal colloquial idiom
glt    A note
}
\end{exe}


\mfssetposformat{\scshape\bfseries\sffamily}
\begin{exe}
    \ex 
    \glinlines{%
    kma t'-əlčqu-(ɣ)in 
*/ .1sg  .1sg.sub-see-.2sg.obj
glt `I saw you.' (S1:71)
        }
\end{exe}


\end{document}

It uses expl3 sequences to store and split the text.

3
  • This is an interesting approach. Are you planning to work on it further? As it stands it fails with embedded inline math (e.g. subscripts), and doesn't wrap lines automatically. Obviously the comments aren't the place to do this kind of review, however.
    – Alan Munn
    Oct 24 at 16:24
  • @AlanMunn github.com/texcicada/expl3/blob/main/glinlines-demo.pdf may be of some distraction, possibly - example 34 does subscripts. No maths, though. Where the examples relate to tex.se questions, I've put in a url. This question/task one is example 5.
    – Cicada
    Oct 25 at 8:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .