Using dplyr, we could replace Other (specify) with NA, then use coalesce:
library(dplyr)
df %>%
mutate(languages = coalesce(na_if(languages, "Other (specify)"), languages2)) %>%
select(languages)
Output
languages
1 Spanish
2 Spanish
3 French
4 German
5 Russian
6 English
7 Portuguese
8 English
A tidyverse option is to use str_replace_all to replace Other (specify) with the value from languages2.
library(tidyverse)
df %>%
mutate(languages = str_replace_all(languages,"Other \\(specify\\)", languages2)) %>%
select(languages)
Data
df <- structure(list(languages = c("Spanish", "Spanish", "Other (specify)",
"Other (specify)", "Other (specify)", "English", "Other (specify)",
"English"), languages2 = c(NA, NA, "French", "German", "Russian",
NA, "Portuguese", NA)), class = "data.frame", row.names = c(NA,
-8L))
Benchmark
However, if you have a lot of data and need something faster, then you might consider base R, which would be faster than dplyr or data.table.

bm <- microbenchmark::microbenchmark(Konrad = mutate(df, languages = case_when(
grepl("^Other,*", languages) & !is.na(languages2) ~ languages2,
TRUE ~ languages
)),
langtang = df %>%
mutate(languages=if_else(languages=="Other (specify)", languages2, languages)),
valentina = df %>%
mutate(languages=if_else(!is.na(languages2), languages2, languages)),
andrew_stringr = df %>%
mutate(languages = str_replace_all(languages,"Other \\(specify\\)", languages2)),
andrew_coalesce = df %>%
mutate(languages = coalesce(na_if(languages, "Other (specify)"), languages2, 'Other (specify)')),
andrew_baseR = {df1 <- df; df1[df1$languages == "Other (specify)", "languages"] <- df1[df1$languages == "Other (specify)", "languages2" ]},
andrew_baseR_with = {df2 <- df; df2$languages <- with( df2, ifelse( languages == "Other (specify)", languages2, languages ) )},
andrew_datatable = {dt <- as.data.table(df); dt[languages == "Other (specify)", languages := languages2 ]},
times = 1000)