# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
#    2014, 2015, 2016, 2018, 2020 Free Software Foundation, Inc.

# This file is part of GNUnited Nations.

# GNUnited Nations is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# GNUnited Nations is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with GNUnited Nations.  If not, see <https://www.gnu.org/licenses/>.

_have-Makefile := $(shell test -f Makefile && echo yes)
_have-Makefile.in := $(shell test -f Makefile.in && echo yes)

ifeq ($(_have-Makefile.in),yes)
ifeq ($(_have-Makefile),)
$(error Please run "./configure" first)
endif
endif

include config.mk

rootdir := ../..

include gnun.mk

ifdef TEAM
-include priorities.mk
endif

VALIDATE-HTML-NOTIFY := $(pkglibexecdir)/validate-html-notify
MAILFAIL := $(pkglibexecdir)/mailfail
MAKE-PROTOTYPE := $(AWK) -f $(pkglibexecdir)/make-prototype.awk
UPDATE-LOCALIZED-URLS := $(pkglibexecdir)/update-localized-urls
ADD-FUZZY-DIFF := $(pkglibexecdir)/add-fuzzy-diff
COPY-MSGID := $(pkglibexecdir)/copy-msgid

PO4A-GETTEXTIZEFLAGS := -o porefs=none
PO4A-TRANSLATEFLAGS := --keep=0

# <style> shouldn't wrap because it may contain generated
# content strings, and wrapping would make them invalid.
PO4A-XHTMLFLAGS := --format=xhtml --master-charset=utf-8 \
		    -o ontagerror=silent \
		    -o "attributes=<meta>content" \
		    -o "untranslated=W<gnun> W<script>" \
		    -o "translated=W<pre> W<style>" \
		    -o "nodefault=<label>" \
		    -o "break=<label>"

# The lower limit of the number of repeating msgids to include in compendium.
COMMONALITY := 10

# The means that (with the `draft-suffix' variable) makes it possible
# to concurrently draft HTML pages from different PO files.
po-suffix :=

# Default period to delay notice about translation being out of date
ifdef GRACE
  OUTDATED-GRACE := 60
else
# no OUTDATED-GRACE by default when there is no GRACE
  OUTDATED-GRACE := -1
endif

# Determine the VCS to use.  The conditional is for efficiency --
# there's no need to spawn a shell to determine the value when all VCS
# operations will be skipped anyway.  For the time being, Bzr is used
# as a fallback since gnu.org is expected to switch soon.
ifneq (,$(findstring yes,$(VCS)))
vcs := $(shell (test -d CVS && echo cvs) \
         || (test -d .svn && echo svn) || echo bzr)
endif

# By default, do not add any files to the repository unless VCS=yes.
ifneq (,$(findstring yes,$(VCS)))
VCSSKIP :=
else
VCSSKIP := echo "SKIP:"
endif

# Assume VALIDATE=yes when unspecified.
ifeq (,$(VALIDATE))
VALIDATE := yes
endif

# Do not validate any files when VALIDATE doesn't contain `yes'.
ifneq (,$(findstring yes,$(VALIDATE)))
VALIDATESKIP :=
else
VALIDATESKIP := echo "SKIP:"
endif

# Do not send warning mails by default unless NOTIFY=yes.
ifneq (,$(findstring yes,$(NOTIFY)))
NOTIFYSKIP :=
else
NOTIFYSKIP := --dry-run
endif

localized-includes := $(localized-ssis) \
		      $(extra-templates) $(optional-templates)

# Issue a command to substitute localized includes in a HTML file.
# Usage: $(call substitute-localized-includes,$(lang)) file.$(lang).html
define substitute-localized-includes
$(SED) --in-place $(foreach inc, $(localized-includes), \
  -e 's%\(<!--#include virtual=\"/$(inc)\)\(.html\" -->\)%\1.$(1)\2%g')
endef

# Issue a command to add some SSI variable definitions at the beginning of
# a proto file; template files don't contain
# the comment about being automatically generated by GNUN, so they are
# not affected.
# Usage: $(call insert-ssi-vars,file.$(lang).html.${draft-suffix},file.$(lang).html)
define insert-ssi-vars
file=$(2); file_base=$$$${file%.*.html}; \
file_base=$$$${file_base#${rootdir}}; \
if $(GREP) '<!-- This file is automatically generated by GNUnited Nations! -->'\
     $(1) &> /dev/null; then \
$(SED) --in-place \
  '1s%^%<!--#set var="ENGLISH_PAGE"\
 value="'"$$$${file_base}"'$(if $(findstring no, \
                             $(MULTIVIEWS)),,.en).html" -->%' $(1); \
fi
endef

### Special variables for the `www' master templates ###
template-dir := $(rootdir)/server
template-translated-base := $(addprefix $(rootdir)/,$(extra-templates) \
                                                    $(optional-templates))
# Guard with a conditional to avoid silent `ls' failures during
# variables' computation when only the package is being built.
ifneq ($(_have-Makefile),yes)
template-pots := $(addsuffix .pot, \
		   $(foreach template,$(extra-templates), \
		     $(dir $(addprefix $(rootdir)/, \
		             $(template)))po/$(notdir $(template)))) \
                 $(addsuffix .pot.opt, \
		   $(foreach template,$(optional-templates), \
		     $(dir $(addprefix $(rootdir)/, \
		             $(template)))po/$(notdir $(template))))
endif
template-lang = $(addsuffix .$(1).html,$(template-translated-base))
template-translated := $(foreach t-lang,$(TEMPLATE_LINGUAS), \
			 $(call template-lang,$(t-lang)))
ifeq ($(VERBOSE),yes)
$(info template-pots = $(template-pots))
$(info template-translated = $(template-translated))
endif
### End of variables declaration (templates) ###

transfer-to-po = \
  $(patsubst ./%,%,$(join $(patsubst %,%po/,$(dir $(1))),$(notdir $(1))))
find-po = $(wildcard $(1).*.po${po-suffix})
### Special variables for `no-grace' items ###
no-grace-items := \
$(addprefix $(rootdir)/,$(call transfer-to-po,$(no-grace-articles)))
no-grace-pot := $(no-grace-items:%=%.pot)
no-grace-po := $(foreach base,$(no-grace-items),$(call find-po,$(base)))
no-grace-translated := $(subst /po/,/,$(no-grace-po:%.po=%.html))
### End of variables declaration (`no-grace' items) ###

### Special variables for sitemap
ifneq ($(sitemap),)
sitemap-base := $(addprefix $(rootdir)/,$(call transfer-to-po,$(sitemap)))
sitemap-pots := $(addsuffix .pot,$(sitemap-base))
sitemap-pos := $(foreach base,$(sitemap-base),$(call find-po,$(base)))
sitemap-translated := $(subst /po/,/,$(sitemap-pos:%.po=%.html))
endif
### End of variables for sitemap

# When a 'po' directory contains a file named 'pot', it is
# used as the whole-directory POT file.  Translations from all *.$lang.po
# are merged to '$lang.po', then it is used as a compendium to distribute
# translations from one 'file.$lang.po' to other $lang translations
# in the same directory.
whole-dir-pots := $(wildcard $(rootdir)/pot)\
  $(foreach dir,$(ALL_DIRS),$(wildcard $(rootdir)/$(dir)/po/pot))

whole-dirs := $(patsubst %/pot,%,$(whole-dir-pots))

### Special variables for all other articles ###
articles := $(foreach dir,$(ALL_DIRS),$(addprefix $(dir)/po/,$(value $(dir))))
articles-pot := $(addprefix $(rootdir)/,$(articles:%=%.pot))
root-articles := $(foreach root-article,$(ROOT), \
		   $(addprefix $(rootdir)/po/,$(root-article)))
root-articles-pot := $(root-articles:%=%.pot)
ALL_POTS := $(articles-pot) $(root-articles-pot)
ALL_BASE := $(ALL_POTS:%.pot=%)
ALL_POS := $(foreach base,$(ALL_BASE),$(call find-po,$(base)))
ALL_POS_BASE := $(ALL_POS:%.po${po-suffix}=%)
articles-translated := $(subst /po/,/,$(ALL_POS:%.po${po-suffix}=%.html))
ifeq ($(VERBOSE),yes)
$(info ALL_POTS = $(ALL_POTS))				
$(info articles-translated = $(articles-translated))
endif
### End of variables for all other articles ###

.PHONY: all merge-whole-dirs no-grace-items
ifeq ($(_have-Makefile),yes)
include Makefile
else
all: compendia $(template-pots) $(template-translated) \
     $(ALL_POTS) merge-whole-dirs \
     $(articles-translated) $(patsubst %.pot,%.translist,$(ALL_POTS)) \
     final-stage
endif
no-grace-items: $(template-pots) $(template-translated) \
		$(no-grace-translated) \
		$(patsubst %.pot,%.translist,$(no-grace-pot)) final-stage

merge-whole-dirs: $(foreach d,$(whole-dirs),merge-whole-dir-$(d))

# List all langugage suffixes existing in the $(1) directory.
define list-langs
$$(sort $$(subst .,,$$(suffix $$(basename $$(wildcard $(1)/*.po)))))
endef

# merge-dir-po: Define a rule to merge PO file $(1) with the per-directory
# compendium $(2) for its language.
#
# $(MSGATTRIB) --fuzzy... $(1): output fuzzy strings from $(article).
# | $(MSGCAT) --use-first... -: output strings that are also available
#                               in $(lang).po.
# | $(MSGATTRIB) --no-fuzzy...: only output up-to-date translations.
# | $(MSGCAT) --use-first... -: remove the previous result line
#                               from $(article).
# $(MSGMERGE) ...: add translations from $(lang).po to $(article).
# $(if ...,true): add fuzzy-diffs when needed.
define merge-dir-po
$(1)-merge: $(1) $(2)
	$(MAILFAIL) $(NOTIFYSKIP) $(transl-addr) \
  "[GNUN Error] $(patsubst $(rootdir)/%,%,$(2)) is not a valid PO file" \
  $(VALIDATESKIP) $(MSGFMT) --check --verbose --output-file=/dev/null $2
	$(MSGATTRIB) --fuzzy --no-obsolete --translated $(1) \
  | $(MSGCAT) --use-first --more-than=1 $(2) - \
  | $(MSGATTRIB) --no-fuzzy --translated \
  | $(MSGCAT) --use-first --less-than=2 -o $(1).tmp $(1) -
	$(MSGMERGE) --backup=none --update --previous -C $(2) \
  $(1).tmp $$(if $$(wildcard $$(basename $$(basename $(1))).pot),\
		$$(basename $$(basename $(1))).pot,\
		$$(basename $$(basename $(1))).pot.opt)
	$$(if $$(and $(WDIFF),$$(call find-language,$$(basename $$(notdir $(2))),\
                                   $(FUZZY_DIFF_LINGUAS))), \
           $(ADD-FUZZY-DIFF) --in-place $(1).tmp,true)
	$$(call check-new-po,$(1))
endef

# Define rules for merging strings from directory $(2) for language $(1).
define la-po-rule
.PHONY: merge-dir-pos-$(2)-$(1)
merge-whole-dir-$(2): merge-dir-pos-$(2)-$(1)
.PHONY: $$(addsuffix -merge,$$(wildcard $(2)/*.$(1).po))
$(2)/$(1).po: $(2)/pot $(wildcard $(2)/*.$(1).po)
	test -f $$@ || touch $$@
	$(MSGMERGE) $(addprefix -C ,$(wildcard $(2)/*.$(1).po)) \
                   --update --previous --backup=none $(2)/$(1).po $(2)/pot
	$$(if $$(and $(WDIFF),$$(call find-language,$(1),\
                                   $(FUZZY_DIFF_LINGUAS))), \
           $(ADD-FUZZY-DIFF) --in-place $(2)/$(1).po,true)

merge-dir-pos-$(2)-$(1): $(2)/$(1).po $(wildcard $(2)/*.$(1).po) \
  $(addsuffix -merge,$(wildcard $(2)/*.$(1).po))
$$(foreach po,$(wildcard $(2)/*.$(1).po),$$(eval \
  $$(call merge-dir-po,$${po},$(2)/$(1).po)))
endef

# Define per-directory compendium-specific rules for directory $(1).
define merge-dir
DIR-LANGS := $(call list-langs,$(1))
merge-whole-dir-$(1): $(1)/pot \
  $$(addsuffix .po,$$(addprefix $(1)/,$$(DIR-LANGS)))
$(1)/pot: $(wildcard $(1)/*.pot) $(wildcard $(1)/*.pot.opt)
	for i in $(wildcard $(1)/*.pot.opt); do cp $$$$i $$$${i%.opt}; done
	msgcat --use-first $$(patsubst %.opt,%,$$^) -o $$@
	$(SED) --in-place \
  '1s,https.*,files in $(patsubst $(rootdir)/%,%,$(1))/,; \
   1,/^"Project-Id-Version:/ \
     s,^\("Project-Id-Version:\).*,\1 $(patsubst $(rootdir)/%,%,$(1))/\\n",' \
   $$@
	for i in $(wildcard $(1)/*.pot.opt); do $(RM) $$$${i%.opt}; done
$$(foreach la,$$(DIR-LANGS),\
  $$(eval $$(call la-po-rule,$$(la),$(1))))
endef

$(foreach d,$(whole-dirs),$(eval $(call merge-dir,$(d))))

.PHONY: substitute-localized-urls update-localized-URLs final-stage

# Unconditional actions to run at the end of build process.
final-stage: substitute-localized-urls

# Regenerate lists of localized URLs in localized-urls.mk
update-localized-URLs:
	$(UPDATE-LOCALIZED-URLS)
-include localized-urls.mk

# Substitute and unsubstitute localised URLs in translations.
substitute-localized-urls: $(articles-translated)
	$(foreach source, ${localized-url-sources}, \
	  langs=$$(echo ${rootdir}/${source}.*.html | \
		   $(SED) 's%${rootdir}/${source}\.%%g;s/\.html//g;s/\*//'); \
	  test -z "$${langs}" \
	    || for l in $${langs}; do \
		 article=${source}; article_dir=/$${article%/*}; \
		 test "x$${article_dir}" != "x/$${article}" || article_dir=""; \
		 po=${rootdir}$${article_dir}/po/$${article##*/}.$${l}.po; \
		  test -f "$${po}" || continue; \
		 script=; \
		 for url in ${${source}-localized-urls}; do \
		   base=$${url%.*}; ext=$${url##*.}; \
		   escaped_url=$$(echo $${url} | $(SED) 's/\./\\./g'); \
		   escaped_localized_url=$$(echo $${url%.*}.$${l}.$${url##*.} \
					    | $(SED) 's/\./\\./g'); \
		   if test -f ${rootdir}$${url%.*}.$${l}.$${url##*.}; then \
		     script="s@$${escaped_url}@$${url%.*}.$${l}.$${url##*.}@g; \
			     $${script}"; \
		   else \
		     script="s@$${escaped_localized_url}@$${url}@g; \
			     $${script}"; \
		   fi; \
		 done; \
		 script=$$(echo $${script} | $(SED) "s/^ *//g"); \
		 file=${rootdir}/${source}.$${l}.html; \
		 if test -n "$${script}"; then \
		   $(SED) "$${script}" $${file} \
		     > $${file}.tmp; \
		   if ! cmp -s $${file} $${file}.tmp; then \
		     cp $${file}.tmp $${file}; \
		   fi; \
		   $(RM) $${file}.tmp; \
		 fi;\
	       done;)

# The command to add a file to the repository.
define add-file
[ -f $(1) ] || (touch $(1) $(2) ; $(VCSSKIP) $(vcs) add $(1))
endef

# Specific case of add-file invocation used in many places
define addfile 
$(call add-file,$@)
endef

# Check whether the changes in the PO file are not trivial;
# update the PO when needed and remove the temporary file.
# It is assumed that the old file is $(1), and the new one is $(1).tmp.
define check-new-po
if ([ ! -f $(1) ] || [ "`diff -U 0 $(1) $(1).tmp \
     | $(GREP) -v "^\(---\|+++\|@@\)" \
     | $(GREP) -v '^[-+]"\(POT-Creation-Date\|PO-Revision-Date\|Language\):' \
     | wc -c`" -ne 0 ]); then \
  mv $(1).tmp $(1) ; \
else \
  $(RM) $(1).tmp; \
fi
endef

### Declarations for compendia ###
#
# All files specifically processed here shall live in compendia/.
# The manually maintained files are:
#
## exclude.pot
#
# Strings that shouldn't go to compendia, like GNUN slots.
#
## master.${lang}.po
#
# Translations that should be used when the specified msgid appears in
# an article; in other words, this file overrides the translations from
# other files.  Other PO files will be rebuilt whenever this file
# changes.
#
# The generated files are:
#
## compendium.pot
#
# Compiled from all POT files; a msgid is included when it appears
# more than $(COMMONALITY) times.
#
## compendium.${lang}.po
#
# Translations of strings from compendium.pot collected
# from all ${lang} PO files.
# 
# This file can be used by the human editor to update master.${lang}.po.
#
## fuzzified-compendium.${lang}.po
#
# Translations from compendium.${lang}.po marked fuzzy even if they
# are not, "previous" msgid is added when absent.
#
# This file is used as a source for missing msgstrs when merging
# the translations; it is not kept in CVS.
#
## master-translated.${lang}.po
#
# Translated current messages from master.${lang}.po.
#
# This file is used to actually update the translations in ${lang};
# it is not kept in CVS.

# Target to rebuild compendia.
.PHONY: compendia

compendia: $(foreach lang,$(TEMPLATE_LINGUAS), \
             compendia/fuzzified-compendium.$(lang).po)

compendia/compendium.pot: $(filter-out %.pot.opt,$(template-pots)) $(ALL_POTS)
	$(addfile)
# The `--use-first' option is needed to avoid stacked "translations"
# of the header (msgid="").
	$(MSGCAT) --use-first --more-than=$(COMMONALITY) -o $@.tmp.pot $^
# msgcat produces an error when handling files with Charset "CHARSET"
# unless all files have .pot extention, so we can't just use .tmp.
	$(MSGCAT) --use-first --less-than=3 -o $@.tmp.pot $@.tmp.pot \
	  compendia/exclude.pot
	$(MSGCAT) --use-first --unique -o $@.tmp.pot $@.tmp.pot \
	  compendia/exclude.pot
	mv $@.tmp.pot $@.tmp
	$(call check-new-po,$@)

define compendium-rules
ALL_$(1)_POS := \
  $(shell ls -t $(filter %.$(1).po,$(ALL_POS) $(template-pots:.pot=.$(1).po)))

MASTER-$(1) :=
MASTER-$(1)-OPTION :=

# Define rules for compendia if there are any translations.
ifneq ($$(ALL_$(1)_POS),)

ifeq ($$(shell test -s compendia/master.$(1).po && echo yes),yes)

# Extract current translations from master compendium.
compendia/master-translated.$(1).po: compendia/master.$(1).po
	-$(MSGATTRIB) --no-fuzzy --translated -o $$@ $$<

MASTER-$(1) := compendia/master-translated.$(1).po
MASTER-$(1)-OPTION := -C $$(MASTER-$(1))

endif # eq ($$(shell test -s compendia/master.$(1).po && echo yes),yes)

compendia/compendium.$(1).po: compendia/compendium.pot $$(ALL_$(1)_POS)
	-$(MSGCAT) --use-first -o $$@.tmp1 $$(ALL_$(1)_POS)
	addfile=no; prev_comp=$$@; \
  test -s $$@ || { addfile=yes; prev_comp=/dev/null; } ; \
  if test -s $$@.tmp1; then \
    $(MSGMERGE) --previous -C $$@.tmp1 -o $$@.tmp $$$${prev_comp} $$<; \
  fi; \
  $(RM) $$@.tmp1; \
  if test -s $$@.tmp; then \
    test $$$${addfile} = no || $$(addfile); \
    $(call check-new-po,$$@); \
  else \
    $(RM) $$@.tmp; \
  fi

compendia/fuzzified-compendium.$(1).po: compendia/compendium.$(1).po
ifeq ($(MSGATTRIB_PREV),yes)
	$(MSGATTRIB) --translated --clear-obsolete --set-fuzzy --previous \
  $$< > $$@
else
	$(MSGATTRIB) --translated --clear-obsolete --set-fuzzy $$< \
  | $(COPY-MSGID) > $$@
endif

COMPENDIUM-$(1)-OPTION := \
  $$(shell test -s compendia/fuzzified-compendium.$(1).po \
    && echo -C compendia/fuzzified-compendium.$(1).po)

endif # ifneq ($$(ALL_$(1)_POS),)
## Check for sitemap compendium.
ifneq ($(sitemap),)
sitemap-compendium-$(1) := $(shell test -f compendia/sitemap-compendium.$(1).po \
 && $(MSGFMT) -c -o /dev/null compendia/sitemap-compendium.$(1).po \
 && echo -C compendia/sitemap-compendium.$(1).po)
ifneq ($$(sitemap-compendium-$(1)),)
$$(addprefix $(rootdir)/,\
   $$(addsuffix .$(1).po,$$(call transfer-to-po,$$(sitemap)))): \
   $$(filter-out -C,$$(sitemap-compendium-$(1)))
endif # neq ($(sitemap-compendium-$(1),)
endif # neq ($(sitemap),)
endef # compendium-rules

$(foreach lang,$(TEMPLATE_LINGUAS),$(eval $(call compendium-rules,$(lang))))

### End declarations for compendia ###

# Compile translations list
define generate-translinks
$(call add-file,$(1).translist); \
file=$(1).translist; cp translist-head.html $$$${file}; \
url_stem=$(subst /po/,/,$(subst $(rootdir),,$(1))); \
article=$(subst /po/,/,$(1)); \
$(GREP) -v "^#" languages.txt \
  | while read language; do \
      code=$$$${language%%	*}; class=""; \
      case $$$${code} in \
        en ) suffix=""; class=' class="original"'; \
             url_suffix=$(if $(findstring no, $(MULTIVIEWS)),"",".en") ;; \
        * ) suffix=.$$$${code}; url_suffix="$$$${suffix}" ;; \
      esac; \
      lang=$$$${language%	*}; lang=$$$${lang#*	}; \
      name=$$$${language##*	}; \
      name=`echo $$$${name} | sed 's,'"'"',\\\\\\\'"'"',g;s,[[:space:]],\&nbsp;,g'`; \
      if test -f $$$${article}$$$${suffix}.html; then \
        echo -n '<span dir="ltr"'$$$$class'>['$$$${code}']&nbsp;<a \
lang="'$$$$code'"\
hreflang="'$$$$code'" href="'$$$${url_stem}$$$${url_suffix}.html'">' \
      >> $$$${file}; \
        echo $$$${name}'</a> &nbsp;</span>' >> $$$${file}; \
      fi; \
    done; \
cat translist-tail.html >> $$$${file}; \
echo '<link rel="alternate" type="text/html" href="'$$$${url_stem}.html'" hreflang="x-default" />' >> $$$${file}; \
$(GREP) -v "^#" languages.txt \
  | while read language; do \
      code=$$$${language%%	*}; class=""; \
      case $$$${code} in \
        en ) suffix=""; class=' class="original"'; \
             url_suffix=$(if $(findstring no, $(MULTIVIEWS)),"",".en") ;; \
        * ) suffix=.$$$${code}; url_suffix="$$$${suffix}" ;; \
      esac; \
      lang=$$$${language%	*}; lang=$$$${lang#*	}; \
      name=$$$${language##*	}; \
      name=`echo $$$${name} | sed 's,'"'"',\\\\\\\'"'"',g'`; \
      if test -f $$$${article}$$$${suffix}.html; then \
        echo -n '<link rel="alternate" type="text/html" lang="'$$$$code'" \
hreflang="'$$$$code'" href="'$$$${url_stem}$$$${url_suffix}.html'" ' \
      >> $$$${file}; \
        echo 'title="'$$$${name}'" />' >> $$$${file}; \
      fi; \
    done; \
echo '<!-- end translist file -->' >> $$$${file};
endef

# Replace SSI directives to include initial translations list with
# directives to include the generated translist file.
define update-translinks-include
if $(GREP) \
       '<!--#include virtual="/server/gnun/initial-translations-list\.html" ' \
       $(subst /po/,/,$(1).html); then \
  $(SED) --in-place \
    's/<!--#include virtual="\/server\/gnun\/initial-translations-list\.html/\
     <!--#include virtual="$(subst /,\/,$(subst $(rootdir),,$(1))).translist/' \
    $(subst /po/,/,$(1).html); \
fi
endef

# The command to generate pot file which performs additional checks
# whether the changes are not substantial (e.g. regarding only
# POT-Creation-Date), in which case the changes are ignored.
define generate-pot
$(MAILFAIL) $(NOTIFYSKIP) $(web-addr) \
  "[GNUN Error] POT generation of $(subst $(rootdir)/,,$@) failed" \
  $(PO4A_GETTEXTIZE) $(PO4A-XHTMLFLAGS) $(PO4A-GETTEXTIZEFLAGS) \
  --master $< --po $@.tmp
$(SED) --in-place \
 '1,/^$$$$/{\
 s@^# SOME DESCRIPTIVE TITLE@# LANGUAGE translation of \
https://www.gnu.org$(subst /po/,/,$(subst $(rootdir),,$(<:.proto=.html)))@;\
 s/^\(# This file is distributed under the same license as the\) \
PACKAGE package./\1 original article./;\
 s@^\("Project-Id-Version:\) PACKAGE VERSION\\n"@\1 \
$(<F:.proto=.html)\\n"@;}' $@.tmp
$(call check-new-po,$@)
touch $@
endef

# The command to generate the translated article $(3) in language $(1)
# in HTML format from a PO file $(2).  The result is further manipulated
# in the recipes.
#
# The PO file must be processed with update-po before passing it
# to this function.
define generate-html
$(PO4A_TRANSLATE) $(PO4A-XHTMLFLAGS) $(PO4A-TRANSLATEFLAGS) \
  --master $$< --po $(2) --localized $(3); \
  $(call substitute-localized-includes,$(1)) $(3); \
  $(AWK) -f $(pkglibexecdir)/sort.awk -v language=$(1) < $(3) > $(3).tmp; \
  mv $(3).tmp $(3)
endef

# Eliminate more than 1 subsequent empty lines before comments in file $(1);
# remove GNUN tags.
define merge-empty-lines
$(SED) --in-place \
  ':egin;N;$$$$!begin;s/\([ \t]*\n[ \t]*\)\{3,\}<!--/\n\n<!--/g' $(1); \
$(SED) --in-place 's/<gnun>/\n&/g;s/<gnun>\([^\n]*\)<\/gnun>/\1/g;s/\n//g' $(1)
endef

comma := ,
# Generate diff file against latest translated revision
# when available (if we have $(WDIFF)).
# Add notice about the translation being out of date.
# The notice is inserted after every line beginning with
# "<!--GNUN: OUT-OF-DATE NOTICE-->"
# If no such lines are found in the file, the notice
# is inserted after the lines starting with
# <!--#include virtual="/server/banner"-->.
# Additional Apache variables are injected at the beginning
# of the file.
define mark-outdated
marker='GNUN: OUT-OF-DATE NOTICE'; \
$(GREP) '^[[:space:]]*<!--'"$$$${marker}-->" $(1) &> /dev/null \
 || marker='#include virtual="\/server\/banner'; \
if $(GREP) '^[[:space:]]*<!--'"$$$${marker}" $(1) &> /dev/null; then \
  original=$(1); original=$$$${original%$(2).html}html; \
$(if $(WDIFF), \
  saved_file=$$$${original%/*.html}/po/$$$${original##*/}; \
  saved_file=$$$${saved_file%html}$(2);\
  diff_file=$$$${saved_file}-diff.html; \
  saved_file=$$$${saved_file}-en.html; \
  if test -f $$$${saved_file}; then \
    $$(call add-file $(comma) $$$${diff_file}); \
    $(SED) "s/</\&lt;/g;s/>/\&gt;/g" < $$$${saved_file} \
	   > $$$${saved_file}.tmp; \
    $(SED) "s/</\&lt;/g;s/>/\&gt;/g" < $$$${original} \
	   > $$$${saved_file}.tmp1; \
    $(SED) "s%<title></title>%<title>$$$${original#$(rootdir)}-diff</title>%" \
	   < diff-page-head.html > $$$${diff_file}; \
    $(WDIFF) --start-delete '<span class="removed"><del><strong>' \
	     --end-delete '</strong></del></span>' \
	     --start-insert '<span class="inserted"><ins><em>' \
	     --end-insert '</em></ins></span>' \
	    $$$${saved_file}.tmp $$$${saved_file}.tmp1 \
	    >> $$$${diff_file}; \
    cat diff-page-tail.html >> $$$${diff_file}; \
    $(RM) $$$${saved_file}.tmp $$$${saved_file}.tmp1; \
  fi; \
  test -f "$$$${diff_file}" || diff_file=""; \
 , diff_file=""; \
 ) \
$(SED) --in-place \
'/^<!--#set var="PO_FILE"/,/<!--#set var="OUTDATED_SINCE"/{\
  s/^<!--#set var="PO_FILE".*/ /;tskip;d;:skip;};\
 /<!--#include virtual="\/server\/outdated/d;\
 1s%^[[:space:]]*%<!--#set var="PO_FILE"\n\
    value='"'<a href=\"$$$${PO#$(rootdir)}\">\n\
           https://www.gnu.org$$$${PO#$(rootdir)}</a>'"'\n \
 --><!--#set var="ORIGINAL_FILE" value="'"$$$${original#$(rootdir)}"'"\n\
 --><!--#set var="DIFF_FILE" value="'"$$$${diff_file#$(rootdir)}"'"\n\
 --><!--#set var="OUTDATED_SINCE"\
 value="'"`$(call extract-outdated-date,$$$$PO) | $(SED) 's/ .*//'`"'"\
 -->%;\
 /^[[:space:]]*<!--'"$$$${marker}"'/a\
<!--#include virtual="/server/outdated.$(2).html" -->' $(1) \
 || true; \
fi
endef

# Get the generation date of the oldest POT with incomplete
# translation stored in `X-Outdated-Since' field of the header.
define extract-outdated-date
($(SED) --quiet \
   '1,/^msgstr/d;/^$$$$/q 0;\
    /^"\(X-\)\?Outdated-Since:/{ \
      s/^"\(X-\)\?Outdated-Since: \(.*\).."$$$$/\2/;p;q 1;}' $(1); \
 test $$$$? != 0 \
)
endef

# Copy POT generation date to `X-Outdated-Since' field
# unless the latter is already present.
# Insert the field after `Content-Transfer-Encoding'
# whither msgmerge would move it.
define insert-outdated-date
($(call extract-outdated-date,$(1)) > /dev/null \
 || $(SED) --in-place \
      '1,/^"POT-Creation-Date:/{\
           p;s/^"POT-Creation-Date:/"X-Outdated-Since:/;h;d};\
       1,/^"Content-Transfer-Encoding:/{\
         s/^\("Content-Transfer-Encoding: \)/\1/;p;t insert;d;\
         :insert x}' $(1))
endef

# Remove `X-Outdated-Since' field from the header.
define remove-outdated-date
$(SED) --in-place \
  '/^msgid ""$$$$/,/^$$$$/{/^"\(X-\)\?Outdated-Since: /d;}' $(1)
endef

define update-po-status
if LC_ALL=C $(MSGFMT) --statistics -o /dev/null $(1) 2>&1 \
    | $(EGREP) '(fuzzy|untranslated)' > /dev/null; then \
  $(call insert-outdated-date,$(1)); \
else \
  $(call remove-outdated-date,$(1)); \
fi
endef

# Check whether language code $(1) is present in list $(2)
# It assumes that language codes can't contain `.';
# it takes into account possibility of partial matches like
# `br' vs. `pt-br'.
define find-language
$(findstring .$(1).,$(addsuffix .,$(addprefix .,$(2))))
endef

# The command to update a PO file ($(1)) from the POT ($(2)).
# $(3) is the language code.
#
# The target is `touched' in order to make `make' to consider it
# up-to-date even if there is nothing to merge.  The default values of
# Content-Type and Content-Transfer-Encoding are replaced with commonly
# used values (or added if the fields are absent).  When appropriate,
# $(ADD-FUZZY-DIFF) is invoked.
#
# When compendia/master-translated.$(3).po is present, its translations override
# the translations from $(1); when compendia/fuzzy-compendium.$(3).po is
# present, it is checked for missing translations.
#
# For sitemaps the special compendium is used when available and fuzzy matching
# is suppressed (it proved to do more harm than good for sitemaps).
define update-po
$(MAILFAIL) $(NOTIFYSKIP) $(transl-addr) \
  "[GNUN Error] $(patsubst $(rootdir)/%,%,$(1)) is not a valid PO file" \
  $(VALIDATESKIP) $(MSGFMT) --check --verbose --output-file=/dev/null $1 \
  || { touch $(1); exit 1; }; \
$(SED) \
  '1{ \
      :egin; /\nmsgstr /b msgstr; N; begin; \
      :msgstr; N; /\nmsgstr .*\n\n/b process; b msgstr; \
      :process; \
      s@\(\nmsgstr.*\n"Content-Type: text/plain; charset\)=CHARSET\\n"\n@\
\1=UTF-8\\n"\n@; \
      /\nmsgstr .*\n"Content-Type:/! \
        s@\(\nmsgstr .*\n\)\n@\1"Content-Type: text/plain; charset=UTF-8\\n"\n\n@; \
      s/\(\n"Content-Transfer-Encoding:\) ENCODING\\n"\n/\1 8bit\\n"\n/; \
      /\nmsgstr .*\n"Content-Transfer-Encoding:/! \
       s/\(\nmsgstr .*\n\)\n/\1"Content-Transfer-Encoding: 8bit\\n"\n\n/; \
    }' $(1) \
  $(if $(MASTER-$(3)), \
     | $(MSGATTRIB) --clear-obsolete \
     | $(MSGCAT) --use-first --more-than=1 $(MASTER-$(3)) - \
     | $(MSGCAT) --use-first --less-than=2 $(1) -) \
  $(if $(findstring $(1), $(sitemap-pos)), | $(MSGATTRIB) --no-fuzzy) \
  | $(MSGMERGE) --previous $(MASTER-$(3)-OPTION) $(COMPENDIUM-$(3)-OPTION) \
    $(if $(findstring $(1), $(sitemap-pos)), $(sitemap-compendium-$(3))) \
    -o $(1).tmp - $(2) \
  && $(call check-new-po,$(1)) && touch $(1) \
  && ($(call update-po-status,$(1)); \
      $(if $(and $(WDIFF),$(call find-language,$(3),$(FUZZY_DIFF_LINGUAS))), \
           $(ADD-FUZZY-DIFF) --in-place $(1);) \
     )
endef

# The command to add "<gnun>" tags in a prototype ($@) where needed.
define add-gnun-tags
$(SED) --in-place -e '/<span[[:space:]]*$$/{:egin;N; />[^\n]*$$/! begin};\
s,<span[[:space:]]\+class="gnun-split"></span>,<gnun></gnun>,g' \
                  -e "s/\$$Date.*\$$/<gnun>\0<\/gnun>/g" $@
endef

# The command to generate a prototype for a page.
define generate-proto
$(MAILFAIL) $(NOTIFYSKIP) $(web-addr),$(devel-addr) \
  "[GNUN Error] Incompatible change in $(subst $(rootdir)/,,$<)" \
  $(MAKE-PROTOTYPE) < $< > $@ || ($(RM) $@ ; exit 1)
$(add-gnun-tags)
endef

# The command to filter out HTML comments
define skip-comments
 $(SED) \
  ':b;/<!--/{:l;/<!--.*-->/!{N;bl};:n;s/<!---->//;tb;s/<!---\?[^-]*/<!--/;bn}'
endef
# The command to extract a meaningful human-readable title from a
# .LANG.html.  Some HTML entities that are sometimes used in titles
# are transformed, to avoid annoying stuff in email subjects.  When
# the title doesn't fit into single line, the first line with
# an ellipsis is used.
#
# The title is base64-encoded as the Subject header must comply with
# RFC 2822, otherwise the message is rejected by the gnu.org servers
# (sysadmin RT #600797).  Used only in article-rules, and only if
# ANNOUNCE=yes.
define extract-title
title_tag='h2'; \
if ! $(skip-comments) $$@ | $(GREP) "<$$$$title_tag" > /dev/null ; \
then title_tag='title'; fi; \
echo -n \`$(skip-comments) $$@ \
  | $(GREP) "<$$$$title_tag"\` | head -n 1 \
  | $(SED) '/<\/'$$$$title_tag'>/!s/$$$$/.../;\
            s/.*<'$$$$title_tag'[^>]*>//;s/<\/'$$$$title_tag'>.*//' \
  | $(SED) 's/\&[mn]dash;/--/g; s/\&nbsp;/ /g' \
  | $(SED) 's,\<span[^>]*>,,g;s,<\/span>,,g' \
  | $(SED) 's/\&..quo;/"/g' | $(BASE64_ENCODE)
endef

# Construct a command sequence suitable for `mail', if the .hook-ann
# file exists.  Only applicable for article-rules, if ANNOUNCE=yes.
# The .hook-ann files are treated differently in the `triggers' rule,
# because it is a valid (and often occurring in practice) scenario to
# have a new translation which is invalid HTML.
define announce
if test -f $$(@F).hook-ann; then \
  echo "echo '<URL:$$(subst $(rootdir),https://www.gnu.org,$$@)>' \
  | $(MAIL) -s '[$(2)] New translation: =?utf-8?B?`$(extract-title)`?=' \
    -a Keywords:$(2)-ann $(ann-addr)" > $$(@F).hook-ann; \
fi
endef

# Suffix for preliminarily generated HTML files.
draft-suffix := .humbly-drafted-by-GNUN

# The command to validate an ordinary article; remove the generated file
# if it's invalid (when VCS operations are enabled).
define validate-article
$(VALIDATESKIP) $(VALIDATE-HTML-NOTIFY) $(NOTIFYSKIP) $(transl-addr) \
  $$@${draft-suffix} || ($(VCSSKIP) $(RM) $$@${draft-suffix}; exit 1)
endef

### Specific rules for templates ###
# All templates are currently either "extra" or "optional".
define extra-template-lang-rules
.PRECIOUS: $(1).$(2).po
$(1).$(2).po: $(1).pot $(MASTER-$(2))
	[ -f $$@ ] || (cp $$< $$@ ; $(VCSSKIP) $(vcs) add $$@)
	$(call update-po,$$@,$$<,$(2))

$(subst /po/,/,$(1)).$(2).html: $(1).proto $(1).$(2).po
	$(call update-po,$(1).$(2).po,$(1).pot,$(2))
	$$(addfile)
	$(call generate-html,$(2),$(1).$(2).po,$$@)
	$(call merge-empty-lines,$$@)
endef

define extra-template-rules
.PRECIOUS: $(1).pot $(1).proto

$(1).pot: $(1).proto
	$$(addfile)
	$$(generate-pot)

$(1).proto: $(subst /po/,/,$(1)).html
	cp $$< $$@
	$$(add-gnun-tags)

$(foreach t-lang,$(TEMPLATE_LINGUAS),\
  $(eval $(call extra-template-lang-rules,$(strip $(1)),$(t-lang))))
endef

define optional-template-lang-rules
ifeq ($(wildcard $(1).$(2).po),)
$(subst /po/,/,$(1)).$(2).html: $(subst /po/,/,$(1)).html
	$$(addfile)
	cp $$< $$@
else
.PRECIOUS: $(1).$(2).po
$(1).$(2).po: $(1).pot.opt $(MASTER-$(2))
	$(call update-po,$$@,$$<,$(2))

$(subst /po/,/,$(1)).$(2).html: $(1).proto $(1).$(2).po
	$(call update-po,$(1).$(2).po,$(1).pot.opt,$(2))
	$(call generate-html,$(2),$(1).$(2).po,$$@)
	$(call merge-empty-lines,$$@)
endif #eq ($(wildcard $(1).$(2).po),)
endef

define optional-template-rules
.PRECIOUS: $(1).pot.opt $(1).proto
$(1).pot.opt: $(1).proto
	$$(addfile)
	$$(generate-pot)

$(1).proto: $(subst /po/,/,$(1)).html
	cp $$< $$@
	$$(add-gnun-tags)

$(foreach t-lang,$(TEMPLATE_LINGUAS),\
  $(eval $(call optional-template-lang-rules,$(strip $(1)),$(t-lang))))
endef

$(foreach template, $(extra-templates),\
  $(eval $(call extra-template-rules, \
	   $(dir $(addprefix $(rootdir)/, \
	           $(template)))po/$(notdir $(template)))))

$(foreach template, $(optional-templates),\
  $(eval $(call optional-template-rules, \
	   $(dir $(addprefix $(rootdir)/, \
	           $(template)))po/$(notdir $(template)))))

### End of rules for templates ###

### Rules for all other articles ###
define article-pot-rules
$(1).proto: $(subst /po/,/,$(1).html)
	$(VALIDATESKIP) $(VALIDATE-HTML-NOTIFY) $(NOTIFYSKIP) $(web-addr) $$<
	test -f $(1).translist || $(call generate-translinks,$(1))
	$(call update-translinks-include,$(1)) 
	$$(generate-proto)

$(1).pot: $(1).proto
	$$(addfile)
	$$(generate-pot)

# We assume that updating the .translist doesn't affect the validity
# of the HTML files that include it; if this is not the case,
# validate-all will complain.
# It might depend on the POs, but then it could be rebuilt too early,
# before some HTML, and therefore contain a wrong list.
$(1).translist: $(filter $(subst /po/,/,$1).%.html,$(articles-translated))
	$(call generate-translinks,$1)
endef

# Produce article HTML file and validate it if needed.
define output-article-html
$(call generate-html,$(2),$(1).po${po-suffix},$$@${draft-suffix}); \
$(call insert-ssi-vars,$$@${draft-suffix},$$@); \
$(call merge-empty-lines,$$@${draft-suffix}) \
  && $(call validate-article,$(1))
endef

define grace-is-over
(timestamp="`$(call extract-outdated-date,$(1))`"; \
 test -n "$$$${timestamp}" \
 && (test $$$$(($(2)+0)) -le 0 \
     || test $$$$((`date +%s` - ($(2)+0)*24*3600)) \
          -ge `date -d "$$$${timestamp}" +%s || echo 0`) \
)
endef

define article-rules
$(1).po${po-suffix}: $(basename $(1)).pot $(MASTER-$(2))
	$(call update-po,$$@,$$<,$(2))

$(subst /po/,/,$(1).html): $(basename $(1)).proto $(1).po${po-suffix}
# If the server templates are missing, skip the generation
# of broken translations.
ifeq (,$(call find-language,$(2),$(TEMPLATE_LINGUAS)))
	@echo 'Note: the "$(2)" language code is not defined in TEMPLATE_LINGUAS.'
else
	$(call update-po,$(1).po${po-suffix},$(basename $(1)).pot,$(2))
	-$(RM) $$@${draft-suffix}
# If GRACE is not defined, which is the usual case for local manual
# builds, update the target and validate the result.
ifndef GRACE
	$(output-article-html)
# Check if the article is not in `no-grace-items'.
else ifneq ($(basename $(1)), \
	    $(findstring $(basename $(1)),$(no-grace-items)))
# If the translation is outdated, compare
# the X-Outdated-Since in the $(1).po${po-suffix} with the current time
# shifted by value $(OUTDATED-GRACE) and insert a notice into the HTML file
# when the grace period is over.
	if ! $(call extract-outdated-date,$(1).po${po-suffix}) > /dev/null; \
        then \
	  $(output-article-html); \
	fi
else
	@echo 'Ignoring grace for article "$(notdir $(basename $(1)))"'
	$(output-article-html)
endif
	if test -f $$@${draft-suffix} ; then \
	  if test -f $$@ -a "`diff $$@ $$@${draft-suffix} | $(GREP) '^[<>] ' \
		    | $(EGREP) -v '[$$$$]Date:' | wc -c`" -eq 0; then \
            touch $$@; \
          else \
            $(if $(findstring .$(ANNOUNCE).,.yes.), \
             $(call add-file,$$@,$$(@F).hook-ann), \
             $(call add-file,$$@)); \
            $(if ${po-suffix}, , mv $$@${draft-suffix} $$@;) \
          fi; \
        fi
# When we provide a ${po-suffix}, we don't generate a real page, we just draft
# it; so we also neither mark the page outdated, nor handle the differences
# against the latest translated version of the original page.
ifeq (${po-suffix},)
	-$(RM) $$@${draft-suffix}
	$(announce) 
	$(if $(findstring $(basename $(1)),$(no-grace-items)), \
	    echo 'Ignoring delay for article "$(notdir $(basename $(1)))"', \
	    if test -f $$@ \
                && $(call grace-is-over,$(1).po,$(OUTDATED-GRACE)); then \
	      PO=$(1).po; $(call mark-outdated,$$@,$(2)); \
	    fi)
	$(call extract-outdated-date,$(1).po) > /dev/null \
	|| ($(call add-file,$(1)-en.html); \
            test -f $(1)-en.html \
		 -a "`diff $(subst /po/,/,$(1:.$(2)=).html) $(1)-en.html \
		   | $(GREP) '^[<>] ' \
		   | $(EGREP) -v '[$$$$]Date:' | wc -c`" -eq 0 \
	    || cp $(subst /po/,/,$(1:.$(2)=).html) $(1)-en.html)
endif
endif # !eq (,$(call find-language,$(2),$(TEMPLATE_LINGUAS)))
endef

$(foreach base,$(ALL_BASE),$(eval $(call article-pot-rules,$(base))))
$(foreach po-base,$(ALL_POS_BASE), \
  $(eval $(call article-rules,$(po-base),$(subst .,,$(suffix $(po-base))))))
### End of rules for all other articles ###

# Assign priorities to translations for report.
ifdef TEAM
TEAM-POS := $(sort $(filter %.${TEAM}.po,${ALL_POS}))
# UnGNUNified translations
TEAM-HTMLS := $(shell echo $(subst /po/,/,$(ALL_POTS:%.pot=%.$(TEAM).html)) \
  | $(SED) "s/[[:space:]]\+/\n/g" \
  | while read f; do \
      po_base=$${f\#\#*/}; po_base=$${po_base%html}po; po_dir=$${f%/*}/po; \
      test -f $$f && { test -f $$po_dir/$$po_base || echo $$f; } done)
optional-pos := \
  $(filter \
    $(foreach template,${optional-templates},\
      ${rootdir}/$(filter-out ./,\
                   $(dir ${template}))po/$(notdir ${template}).${TEAM}.po), \
    ${TEAM-POS})
optional-htmls := \
  $(sort $(filter \
    $(foreach template,${optional-templates},\
      ${rootdir}/$(filter-out ./,\
                   $(dir ${template}))/$(notdir ${template}).${TEAM}.html), \
    ${TEAM-HTMLS}))
priority-articles-pos := \
  $(filter \
    $(foreach article,${priority-articles},\
      ${rootdir}/$(filter-out ./,\
                   $(dir ${article}))po/$(notdir ${article}).${TEAM}.po), \
    ${TEAM-POS})
priority-articles-htmls := \
  $(sort $(filter \
    $(foreach article,${priority-articles},\
      ${rootdir}/$(filter-out ./,\
                   $(dir ${article}))/$(notdir ${article}).${TEAM}.html), \
    ${TEAM-HTMLS}))
important-articles-pos := \
  $(filter \
     $(foreach article,${important-articles},\
       ${rootdir}/$(filter-out ./,\
                    $(dir ${article}))po/$(notdir ${article}).${TEAM}.po), \
     ${TEAM-POS})
important-articles-htmls := \
  $(sort $(filter \
     $(foreach article,${important-articles},\
       ${rootdir}/$(filter-out ./,\
                    $(dir ${article}))/$(notdir ${article}).${TEAM}.html), \
     ${TEAM-HTMLS}))
important-dir-pos := \
$(filter-out ${priority-articles-pos} ${important-articles-pos}\
             ${optional-pos},\
  $(filter $(addsuffix /%, \
             $(addprefix ${rootdir}/,${important-directories})), \
    ${TEAM-POS}))
important-dir-htmls := \
$(sort $(filter-out ${priority-articles-htmls} ${important-articles-htmls},\
  $(filter $(addsuffix /%, \
             $(addprefix ${rootdir}/,${important-directories})), \
    ${TEAM-HTMLS})))
other-pos := \
  $(filter-out ${priority-articles-pos} ${important-articles-pos} \
    ${important-dir-pos}, \
    $(shell find $(rootdir) -name '*.$(TEAM).po' \
              ! -path '$(template-dir)/gnun/*'))
other-htmls := \
  $(sort $(filter-out ${priority-articles-htmls} ${important-articles-htmls} \
    ${important-dir-htmls} ${optional-htmls}, ${TEAM-HTMLS}))

# Function to report a group of PO files.
define report-pos
@$(if $(1)$(strip $(3)), $(if $(2), echo "  "$(strip $(2)); echo;)) \
 $(if $(1), \
    for i in $(1); do \
      echo -n "$${i#${rootdir}/}: "; \
      LC_ALL=C $(MSGFMT) --statistics -o /dev/null $$i 2>&1 \
                 | $(SED) ':egin;$$s/\n[[:space:]]*/; /g;N;begin'; \
    done | $(EGREP) '(fuzzy|untranslated)[^:]*$$' \
    || echo "All translations seem to be up-to-date."; $(if $(2), echo;) \
   )\
 $(if $(strip $(3)), echo Translations to convert to PO files:; echo; \
   for html in $(3); do echo $${html#${rootdir}/}; done; echo)
endef
endif # def TEAM

# Special target to check which translations need updating.
.PHONY: report
report: 
ifndef TEAM
	$(error Please specify a language code, for example TEAM=fr)
endif
ifeq (,$(call find-language,$(TEAM),$(sort $(TEMPLATE_LINGUAS))))
	@echo "There are no translations for language $(TEAM)."
else
ifeq (,${priority-articles}${important-articles}${important-directories})
	$(call report-pos,${other-pos},,${other-htmls})
else #!eq (,${priority-articles}${important-articles}${important-directories})
	$(call report-pos,${priority-articles-pos},Priority Articles,\
                          ${priority-articles-htmls})
	$(call report-pos,${important-articles-pos},Important Articles,\
	                  ${important-articles-htmls})
	$(call report-pos,${important-dir-pos},\
	  Other Articles from Important Directories,\
	                  ${important-dir-htmls})
	$(call report-pos,${other-pos},Other Translations,${other-htmls})
endif #!eq (,${priority-articles}${important-articles}${important-directories})
endif # !eq (,$(call find-language,$(TEAM),$(sort $(TEMPLATE_LINGUAS))))

# Special target to compile report about number of outdated translations
# for all languages.
.PHONY: reports-summary
reports-summary:
	@echo Outdated translations:
	@$(foreach lang,$(TEMPLATE_LINGUAS), echo -n "$(lang): "; \
	   $(MAKE) report TEAM=$(lang) \
	   | $(EGREP) '(fuzzy|untranslated)[^:]*$$' \
	   | $(EGREP) -v 'html$$' | wc -l; )
	@echo; echo UnGNUNified translations:
	@$(foreach lang,$(TEMPLATE_LINGUAS), echo -n "$(lang): "; \
	   $(MAKE) report TEAM=$(lang) \
	   | $(EGREP) 'html$$' | wc -l; )

# Special target to touch all the prerequisites of the targets that
# failed HTML validation and thus to trigger a rebuild in the next
# run.
.PHONY: triggers
triggers:
ifeq (,$(wildcard *.hook))
	@echo "No triggers to process; build apparently successful."
else
# Execute the command recorded in every .hook file.
	@for t in *.hook; do \
	  echo -n "Processing $$t... " && bash $$t && echo done.; \
	done
# Delete all *.hook files, or else there will be useless rebuilds
# every time updating the `Date' timestamp in .LANG.html.
	$(RM) *.hook
endif
# Handle announcements of new translations.
ifeq (,$(wildcard *.hook-ann))
	@echo "No new translations."
else
# If the .hook-ann file contains "gnun-do-not-delete-me", it means
# that the build failed in article-rules while checking the PO file.
# The .LANG.html is empty, not suitable to be announced, so do nothing
# in this case.
	@for t in *.hook-ann; do \
	  if ! $(GREP) 'gnun-do-not-delete-me' $$t > /dev/null; then \
	    echo -n "Sending announcement for $$t... " \
	    && bash $$t && echo done.; $(RM) $$t; \
	  fi; \
	done
endif
# Validate all articles under GNUN's control.
# This is needed because GNUN doesn't track included files,
# and the page may become invalid when an included file changes.
.PHONY: validate-all
validate-all:
	for html in $(subst /po/,/,$(ALL_POTS:%.pot=%.html)); do \
          echo Validating $${html}...; \
          $(VALIDATE-HTML-NOTIFY) $(NOTIFYSKIP) ${web-addr} $${html} \
          || exit 1; \
        done
# Note that the HTML file with translation may or may not exist yet,
# so we should test it; also, if the HTML file is not newer than PO,
# it may be a very old translation whose format validation is not supported.
	for html in $(articles-translated); do \
          po=$${html%html}po; po=$${po%/*}/po/$${po##*/}; \
          if test -f $${html} && test $${html} -nt $${po}; then \
            echo Validating $${html}...; \
            $(VALIDATE-HTML-NOTIFY) $(NOTIFYSKIP) ${transl-addr} $${html} \
            || exit 1; \
          else \
            if test -f $${html} ; then \
              echo File $${html} is not newer than $${po}.; \
            else \
              echo No $${html} exist.; \
            fi; \
          fi; \
        done

### Everything that has a beginning has an end. ###
