Neural machine translation when the data barely exists

The default advice on Neural Machine Translation (NMT) has become “throw it at GPT-4 and hope for the best.” That works well enough for French to English. It falls apart on a low-resource language like Dongxiang. Fourteen years of wrestling with complex logic has taught me that a general-purpose LLM is not a substitute for touching the architecture yourself.

Some languages are barely acknowledged by mainstream models, and building a translator for one is closer to preservation work than product work. Below is how we fine-tuned Meta’s NLLB-200 (No Language Left Behind) for a minority language, and the Neural Machine Translation bottlenecks that actually cost us time.

Why the standard NMT recipe fails here

The common mistake is assuming more data always means better results. Working through those senior dev insights on applied statistics made the trade-off obvious: in a low-resource setting, noise hurts you more than volume helps. If 30% of your training set is hallucinated text or misaligned Chinese-Dongxiang pairs, the model does not come out slightly less accurate. It comes out broken.

Step 1: cleaning the bilingual dataset

Normalization comes first. Raw scraped text fed into a transformer gives you nothing usable, so the pipeline has to strip extra whitespace and standardize punctuation before anything else. This is the Python preprocessing I use to keep the two scripts apart and cut the noise.

import re
import pandas as pd

def clean_dxg(s: str) -> str:
    # Restrict to Latin characters for Dongxiang
    s = re.sub(r"[^A-Za-z\s,\.?]", " ", s)
    s = re.sub(r"\s+", " ", s).strip()
    return s

def clean_zh(s: str) -> str:
    # Restrict to Chinese characters for Mandarin
    s = re.sub(r"[^\u4e00-\u9fff,。?]", "", s)
    return s.strip()

# Naive approach: Just splitting lines.
# Fix: Ensure sentence-level alignment before training.

Neural machine translation: the tokenizer trap

Most people assume a new language means retraining the tokenizer from scratch. Don’t. NLLB’s Unigram tokenizer holds up better than you would expect. Before you spend days on SentencePiece, measure subword fertility, the average number of tokens per word. A rate around 1.9 to 2.2 on a new language means the default tokenizer is coping. If it spikes past 10, you have a fragmentation problem.

Step 3: registering the language ID

NLLB wants explicit language tags in src_lang and tgt_lang. A language missing from Meta’s predefined list has no way to be encoded, so you add the token and resize the embedding matrix by hand. Get careless with the index and this is where it breaks.

from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
import torch

def bbioon_register_language(model_name, new_lang_code):
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
    
    # Add special token
    tokenizer.add_special_tokens({"additional_special_tokens": [new_lang_code]})
    model.resize_token_embeddings(len(tokenizer))
    
    # Initialize new embedding with small variance
    new_id = tokenizer.convert_tokens_to_ids(new_lang_code)
    embed_dim = model.model.shared.weight.size(1)
    model.model.shared.weight.data[new_id] = torch.randn(embed_dim) * 0.02
    
    return model, tokenizer

Step 4: training with Adafactor

For fine-tuning a transformer on a single GPU, an A100 in our case, I reach for the Adafactor optimizer every time. It skips the full momentum vectors that Adam keeps, so it uses less memory and you can push the batch size higher before CUDA runs out.

A war story: on a similar NMT task I used standard AdamW and spent six hours chasing what I was certain were race conditions. They were memory leaks from the optimizer state. Adafactor would have saved me the afternoon.

Evaluation: why BLEU scores lie

We hit a BLEU-4 of 44.00 on Dongxiang, which reads well on a slide. Automatic metrics are only a proxy, though, and with a small corpus you also have to watch for drift detection. The model overfits to the sentence structures it was shown, so the score holds up right until a user writes something the corpus never contained.

If this kind of work is eating your billable hours, I can take it on. I have been building WordPress and backend integrations since the 4.x days.

What actually decides the outcome

Model size is not what makes a Dongxiang translation system work. A precise data pipeline and a stable fine-tuning run are. Meta’s NLLB-200 documentation is a reasonable starting point, but most of the real work sits in the requirements.txt and the preprocessing scripts. Ship something small and get native speakers to read the output before you trust a benchmark.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.