My approach would be to throw together some nested ifelse() statements if there are only a handful of alternatives to deal with (as is the case with population size).
ifelse(x>=1e12, sprintf("%.2f trillion", x/1e12),
ifelse(x>=1e9, sprintf("%.2f billion", x/1e9),
ifelse(x>=1e6, sprintf("%.2f million", x/1e6),
format(x, big.mark=","))))
If df is your data.frame then yours would be
df$pop_text <-
ifelse(df$population>=1e12, sprintf("%.2f trillion", df$population/1e12),
ifelse(df$population>=1e9, sprintf("%.2f billion", df$population/1e9),
ifelse(df$population>=1e6, sprintf("%.2f million", df$population/1e6),
format(df$population, big.mark=","))))
An ifelse() evaluates the condition in the first argument (e.g. is x over 1 trillion?) and, if true, performs the operation in the second argument and moves to the next value of x, and if false, performs the operation in the third argument and moves to the next value of x. Placing ifelse() calls in the third argument of another means that second ifelse() gets evaluated if the first returned a false.