Don't tell Joseph.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\declarecountry}{m}
{
\prop_new:c { g_tacet_cities_#1_prop }
}
\NewDocumentCommand{\createcity}{mmm}
{
\prop_gput:cnn { g_tacet_cities_#1_prop } { #2 } { #3 }
}
\NewDocumentCommand{\printcity}{mm}
{
#2,~#1:~\prop_item:cn { g_tacet_cities_#1_prop } { #2 }
}
\NewDocumentCommand{\printcities}{m}
{
\tacet_cities_print:n { #1 }
}
\seq_new:N \l__tacet_cities_country_seq
\cs_new_protected:Nn \tacet_cities_print:n
{
\seq_clear:N \l__tacet_cities_country_seq
\prop_map_inline:cn { g_tacet_cities_#1_prop }
{
\seq_put_right:Nx \l__tacet_cities_country_seq { ##1 }
}
\seq_sort:Nn \l__tacet_cities_country_seq
{
\tacet_city_if_before:nnTF { ##1 } { ##2 }
{ \sort_return_same: }
{ \sort_return_swapped: }
}
\section{#1}
\seq_map_inline:Nn \l__tacet_cities_country_seq
{
\noindent
##1:~\prop_item:cn { g_tacet_cities_#1_prop } { ##1 }
\par
}
}
\prg_new_conditional:Nnn \tacet_city_if_before:nn { p,T,F,TF }
{% I hope the LaTeX3 police won't catch me
\int_compare:nTF { \pdftex_strcmp:D { #1 } { #2 } < 0 }
{
\prg_return_true:
}
{
\prg_return_false:
}
}
\ExplSyntaxOff
\begin{document}
\declarecountry{Italy}
\createcity{Italy}{Venice}{Some facts about Venice}
\createcity{Italy}{Milan}{Some facts about Milan}
\createcity{Italy}{Florence}{Some facts about Florence}
\createcity{Italy}{Rome}{Some facts about Rome}
\printcity{Italy}{Venice}
\printcities{Italy}
\end{document}

A bit more complicated
Cities in different countries tend to have bizarre accents over them and the above code wouldn't work with them.
Here's an extended version, where for cities with diacritics in their name, you need to supply also a key for sorting and calling (see the Düsseldorf) example.
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\declarecountry}{m}
{
\seq_new:c { g_tacet_cities_#1_seq }
}
\NewDocumentCommand{\createcity}{mmm}
{
\tacet_cities_create:nnn { #1 } { #2 } { #3 }
}
\NewDocumentCommand{\printcity}{mm}
{
\prop_item:cn { g_tacet_cities_city_#1/#2_prop } { name }
,~
#1:~
\prop_item:cn { g_tacet_cities_city_#1/#2_prop } { text }
}
\NewDocumentCommand{\printcities}{m}
{
\tacet_cities_print:n { #1 }
}
\prop_new:N \l__tacet_cities_temp_prop
\tl_new:N \l__tacet_cities_temp_tl
\seq_new:N \l__tacet_cities_country_seq
\keys_define:nn { tacet/cities }
{
name .code:n = \prop_put:Nnn \l__tacet_cities_temp_prop { name } { #1 },
key .code:n = \prop_put:Nnn \l__tacet_cities_temp_prop { key } { #1 },
unknown .code:n = \prop_put:NnV \l__tacet_cities_temp_prop { name } \l_keys_key_tl,
}
\cs_new_protected:Nn \tacet_cities_create:nnn
{
\prop_clear:N \l__tacet_cities_temp_prop
\keys_set:nn { tacet/cities } { #2 }
\prop_if_in:NnTF \l__tacet_cities_temp_prop { key }
{
\prop_get:NnN \l__tacet_cities_temp_prop { key } \l__tacet_cities_temp_tl
}
{
\prop_get:NnN \l__tacet_cities_temp_prop { name } \l__tacet_cities_temp_tl
\prop_put:NnV \l__tacet_cities_temp_prop { key } \l__tacet_cities_temp_tl
}
\seq_gput_right:cV { g_tacet_cities_#1_seq } \l__tacet_cities_temp_tl
\prop_put:Nnn \l__tacet_cities_temp_prop { text } { #3 }
\prop_new:c { g_tacet_cities_city_#1/\l__tacet_cities_temp_tl _prop }
\prop_gset_eq:cN
{ g_tacet_cities_city_#1/\l__tacet_cities_temp_tl _prop }
\l__tacet_cities_temp_prop
}
\cs_new_protected:Nn \tacet_cities_print:n
{
\seq_set_eq:Nc \l__tacet_cities_country_seq { g_tacet_cities_#1_seq }
\seq_sort:Nn \l__tacet_cities_country_seq
{
\tacet_city_if_before:nnTF { ##1 } { ##2 }
{ \sort_return_same: }
{ \sort_return_swapped: }
}
\section{#1}
\seq_map_inline:Nn \l__tacet_cities_country_seq
{
\noindent
\prop_item:cn { g_tacet_cities_city_#1/##1_prop } { name }
:~
\prop_item:cn { g_tacet_cities_city_#1/##1_prop } { text }
\par
}
}
\prg_new_conditional:Nnn \tacet_city_if_before:nn { p,T,F,TF }
{% I hope the LaTeX3 police won't catch me
\int_compare:nTF { \pdftex_strcmp:D { #1 } { #2 } < 0 }
{
\prg_return_true:
}
{
\prg_return_false:
}
}
\ExplSyntaxOff
\begin{document}
\declarecountry{Italy}
\createcity{Italy}{Venice}{Some facts about Venice}
\createcity{Italy}{Milan}{Some facts about Milan}
\createcity{Italy}{Florence}{Some facts about Florence}
\createcity{Italy}{Rome}{Some facts about Rome}
\declarecountry{Germany}
\createcity{Germany}{Furtwangen}{A town in the Black Forest}
\createcity{Germany}{Bielefeld}{Does it exist?}
\createcity{Germany}{Berlin}{A big city}
\createcity{Germany}{Darmstadt}{Hosted the TUG Conference}
\createcity{Germany}{
name=Düsseldorf,
key=Dusseldorf,
}{This has the \emph{umlaut}}
\printcity{Italy}{Venice}
\printcity{Germany}{Dusseldorf}% the key!
\printcities{Italy}
\printcities{Germany}
\end{document}

expl3. – egreg Mar 04 '17 at 11:13